<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Just Some Code</title>
 <link href="https://canro91.github.io/atom.xml" rel="self"/>
 <link href="https://canro91.github.io/"/>
 <updated>2026-05-10T09:39:10+00:00</updated>
 <id>https://canro91.github.io</id>
 <author>
   <name>Cesar Aguirre</name>
   <email></email>
 </author>

 
 <entry>
   <title>Kangaroos And The Road Sign (A 100-Word Fiction Story Inspired By a Photo)</title>
   <link href="https://canro91.github.io/2026/05/09/Kangaroos/"/>
   <updated>2026-05-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/09/Kangaroos</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Here’s May photo prompt from &lt;a href=&quot;https://100wordstory.org/photo-prompt/&quot;&gt;100 word story&lt;/a&gt; and my story.&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2026-05-09-Kangaroos/RoadSign.png&quot; alt=&quot;Nullarbor Warning Sign&quot; /&gt;
    &lt;figcaption&gt;Nullarbor Warning Sign. Photo by Chris Fithall on flickr.com&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;When some friends came over and you had some beers…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every five minutes I asked, &lt;em&gt;“Dad, when are we going to see kangaroos?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was 5 or 6. I had read every kangaroo book in the library.&lt;/p&gt;

&lt;p&gt;My dad had rented a car. I didn’t care about Sydney and the Opera House. I only wanted to see kangaroos.&lt;/p&gt;

&lt;p&gt;I don’t remember where we were, but we heard, &lt;em&gt;puff!&lt;/em&gt;…and the car stopped. OMG! Smoke everywhere, hot as hell. “And the kangaroos?” I started to cry.&lt;/p&gt;

&lt;p&gt;My dad crossed the road and took this picture. That was the only kangaroo I saw in Australia.&lt;/p&gt;

&lt;p&gt;I’ll never rent a car. Ever.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Friday Links: Coding sucks, inverse laws of robotics, and Chrome surprises</title>
   <link href="https://canro91.github.io/2026/05/08/FridayLinks/"/>
   <updated>2026-05-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/08/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; &lt;a href=&quot;https://www.stvn.sh/writing/programming-still-sucks-fqffhyp&quot;&gt;Coding sucks&lt;/a&gt; (9min) It’s not like building a house, but like jumping into a ship, nobody knows where it goes…And no, AI didn’t take our jobs. It was something else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Isaac Asimov created three laws for robots to interact with humans. If you’ve watched &lt;em&gt;I Robot,&lt;/em&gt; you already know them. And here are &lt;a href=&quot;https://susam.net/inverse-laws-of-robotics.html&quot;&gt;three inverse laws for humans to interact with AI&lt;/a&gt; (9min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Did you know that &lt;a href=&quot;https://www.thatprivacyguy.com/blog/chrome-silent-nano-install/&quot;&gt;Chrome installs a 4GB AI model&lt;/a&gt; (30min) without asking you?…And if you delete it, it installs it again. Surprise!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; I already knew about robots.txt, but there’s &lt;a href=&quot;https://www.vzqk50.com/blog/scraps/a-well-known-complaint/&quot;&gt;a whole folder to host those files&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/05/04/StarWars/&quot;&gt;why Star Wars is the best saga&lt;/a&gt; (3min) and &lt;a href=&quot;/2026/05/02/RandomIdeas/&quot;&gt;7 interesting but random ideas I found&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Street-Smart Coding&lt;/a&gt;, 30 lessons to help you code like a pro. From Googling to clear communication, it shares the lessons to help you stand out in the age of AI.&lt;/p&gt;

&lt;p&gt;May the Force be with you,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Lessons I Wish I Knew When I Was 25</title>
   <link href="https://canro91.github.io/2026/05/07/Lessons/"/>
   <updated>2026-05-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/07/Lessons</id>
   <content type="html">&lt;p&gt;#1. Buy a domain and choose a professional handle. And no, a hacker-like handle isn’t cool.&lt;/p&gt;

&lt;p&gt;#2. Start that blog. Seriously! Write anywhere. You’re going to like it.&lt;/p&gt;

&lt;p&gt;#3. Open a brokerage account and start investing. Go with a broad market index fund and don’t worry about ups and downs.&lt;/p&gt;

&lt;p&gt;#4. Take care of your body. Go to bed early. Exercise. Eat the right food, in the right amount.&lt;/p&gt;

&lt;p&gt;#5. Don’t try to fit in. If you feel alone, there’s nothing wrong with you. Build a place where you fit in.&lt;/p&gt;

&lt;p&gt;#6. Don’t force people into your life. That applies to friends and relationships.&lt;/p&gt;

&lt;p&gt;#7. Ask for help. Figuring out things on your own is satisfying. But with help, you’ll get results faster.&lt;/p&gt;

&lt;p&gt;#8. You can’t fix the world or people around you. And that’s OK. Often, they don’t want to be saved.&lt;/p&gt;

&lt;p&gt;#9. Be present for your family. And being around and providing for them isn’t enough.&lt;/p&gt;

&lt;p&gt;#10. If something feels off about work and life, you’re most likely right. &lt;a href=&quot;/2025/05/07/LifeLesson/&quot;&gt;Come up with your plan&lt;/a&gt; or work on somebody else’s plan—you’re not going to like it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If any of my lessons resonated, check out &lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;these 10 ideas that changed my life and could change yours.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Simpler Way To Start The Zettelkasten Method</title>
   <link href="https://canro91.github.io/2026/05/06/HowToMakeNotesAndWrite/"/>
   <updated>2026-05-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/06/HowToMakeNotesAndWrite</id>
   <content type="html">&lt;p&gt;Search for “Zettelkasten” and you’ll drown in endless breakdowns of note types and apps.&lt;/p&gt;

&lt;p&gt;But the Zettelkasten method isn’t complicated.
&lt;em&gt;How to Make Notes and Write&lt;/em&gt; by Dan Allosso shows a simple way to adopt it.&lt;/p&gt;

&lt;p&gt;Here are five lessons I’ve learned from reading it:&lt;/p&gt;

&lt;h2 id=&quot;1-avoid-the-mere-exposure-effect&quot;&gt;#1. Avoid the mere-exposure effect.&lt;/h2&gt;

&lt;p&gt;Seeing a concept frequently doesn’t mean you understand it.
You truly understand something when you write about it.
&lt;a href=&quot;/2025/01/20/WritingToLearn/&quot;&gt;Writing makes you think&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-read-with-a-project-in-mind&quot;&gt;#2. Read with a project in mind.&lt;/h2&gt;

&lt;p&gt;Don’t extract everything from a book in one pass.
Record what’s useful for your current project.
&lt;a href=&quot;/2026/04/18/Rereading/&quot;&gt;You can always reread&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-focus-on-the-process-not-the-tools&quot;&gt;#3. Focus on the process, not the tools.&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“What’s the best plugin for Obsidian?”&lt;/em&gt;
&lt;em&gt;“When should I use this note type?”&lt;/em&gt;
Arrggg!&lt;/p&gt;

&lt;p&gt;Forget about tweaking fancy tools.
Niklas Luhmann, the Zettelkasten godfather, didn’t even have a computer.
He only used pen and paper—and a typewriter.&lt;/p&gt;

&lt;p&gt;The process is rather simple:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/01/20/WritingInBooks/&quot;&gt;Highlight or underline&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Record data as you find it.&lt;/li&gt;
  &lt;li&gt;Interpret and make it relevant for your projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;4-use-only-two-types-of-notes&quot;&gt;#4. Use only two types of notes.&lt;/h2&gt;

&lt;p&gt;Use &lt;em&gt;Source&lt;/em&gt; notes to record data.
Quotations, definitions, ideas, concepts.
Source notes collect evidence of what you find.&lt;/p&gt;

&lt;p&gt;Use &lt;em&gt;Point&lt;/em&gt; notes to write conclusions.
Anything you want to turn into knowledge counts as a note.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Point&lt;/em&gt; reminds you to write a single observation in a note.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2020/11/18/HowToTakeSmartNotes/&quot;&gt;How to Take Smart Notes&lt;/a&gt; calls them &lt;em&gt;literature&lt;/em&gt; and &lt;em&gt;permanent&lt;/em&gt; notes.
But they’re the same thing.&lt;/p&gt;

&lt;h2 id=&quot;5-originality-is-also-finding-new-connections&quot;&gt;#5. Originality is also finding new connections.&lt;/h2&gt;

&lt;p&gt;Without connections, notes are just a collection of trivia.
That’s why &lt;a href=&quot;/2026/04/14/Zettelkasten/&quot;&gt;my first Zettelkasten attempt didn’t work&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stop Wasting Your Readers&apos; Time</title>
   <link href="https://canro91.github.io/2026/05/05/WasteTime/"/>
   <updated>2026-05-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/05/WasteTime</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Telling readers you don’t want to waste their time is already wasting it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last week in a grocery line, I picked up a self-help book from the nearby shelves. In the introduction, the author said he liked getting straight to the point without wasting time.&lt;/p&gt;

&lt;p&gt;Then today I started a note-taking book. After listing two well-known note takers, the author gave the same disclaimer. Arrggg!&lt;/p&gt;

&lt;p&gt;What if, instead of &lt;a href=&quot;/2025/05/09/BleedInTheFirstLine/&quot;&gt;opening your book or post&lt;/a&gt; with a disclaimer, you went directly to the point? That’s how you don’t waste people’s time.&lt;/p&gt;

&lt;p&gt;That’s the main lesson from &lt;a href=&quot;/2026/01/07/SmartBrevity/&quot;&gt;Smart Brevity&lt;/a&gt; and that’s the whole point behind &lt;a href=&quot;/2025/12/27/MiniBookModel/&quot;&gt;mini books&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Reasons Why Star Wars Is One of The Best Movie Franchises</title>
   <link href="https://canro91.github.io/2026/05/04/StarWars/"/>
   <updated>2026-05-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/04/StarWars</id>
   <content type="html">&lt;p&gt;To warm up &lt;a href=&quot;/2025/06/17/TenIdeas/&quot;&gt;my idea muscles&lt;/a&gt; and celebrate May 4th, here’s why Star Wars is the best saga:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. A proven story.&lt;/strong&gt; A constant fight between good and evil, light and darkness, bright side and dark side. We see that in almost all religions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Full of action, alien species, locations, futuristic weapons.&lt;/strong&gt; You’d never imagine how diverse life is &lt;em&gt;in a galaxy far, far away.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A story for all family members.&lt;/strong&gt; It’s a war saga, but no matter the battles, treason, and plots, there’s no blood or sex on screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Well-known cast.&lt;/strong&gt; I only imagine our moms and aunts’ generation going crazy seeing Harrison Ford on the screen in the original trilogy. Natalie Portman, Ewan McGregor, Liam Neesson…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Legendary quotes that became part of the pop culture.&lt;/strong&gt; &lt;em&gt;“May the Force be with you,”&lt;/em&gt; &lt;em&gt;“Do or do not. There’s no try.”&lt;/em&gt; Even I’ve adopted &lt;a href=&quot;/2024/11/16/QuotesToLiveBy/&quot;&gt;one Star Wars line as a mantra&lt;/a&gt;…and couldn’t help but include some references in &lt;a href=&quot;/books&quot;&gt;my books&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Reflection of human nature.&lt;/strong&gt; With the right or wrong motivations, we could become what we fight. The fear of losing a loved one turned Anakin into a cruel being. I mean, executing Order 66 without hesitation?!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. The best and cruelest of all villains.&lt;/strong&gt; Even if you haven’t seen the movies, you know about Darth Vader.&lt;/p&gt;

&lt;p&gt;His mysterious suit, his breathing sound, his tone of voice, his motivations, his ruthless methods make him terrifying. &lt;em&gt;“Be careful not to choke on your own aspirations…“&lt;/em&gt; right after choking someone? The best villain.&lt;/p&gt;

&lt;p&gt;By the way, here’s what I’d write &lt;a href=&quot;/2025/05/04/DarthVader/&quot;&gt;if I were Darth Vader brand strategist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. The most iconic plot twist in the history of cinema.&lt;/strong&gt; &lt;em&gt;“No…I am your father.”&lt;/em&gt; Toy Story made a parody and The Simpsons made fun of that scene.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. More than a trilogy.&lt;/strong&gt; It’s such a great story that has given birth to prequels, sequels, spin-offs, videogames, animated movies, comics. If you’re a fan, you’ll never get bored of Star Wars.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Grogu and Mando.&lt;/strong&gt; Whoever came up with the idea of a cute, baby Yoda is a genius. I bet they should already have a good raise or stock package. Mandalorian has brought new fans to the saga. &lt;em&gt;“This is the way!”&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A &quot;Hell Yes&quot; Can Always Become a Hard No</title>
   <link href="https://canro91.github.io/2026/05/04/HellYes/"/>
   <updated>2026-05-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/04/HellYes</id>
   <content type="html">&lt;p&gt;When facing a decision or hard choice:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If it doesn’t make say “Hell yes,” say no.&lt;/em&gt;
Credits to &lt;a href=&quot;/2026/04/14/AnythingYouWant/&quot;&gt;Derek Sivers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In past weeks, a journalist found &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;my rules for AI coding&lt;/a&gt;.
He invited for an interview to write a story about it.
A story featuring me in a tech news site? “Hell yes.”&lt;/p&gt;

&lt;p&gt;After the interview, I had to send a photo of my ID, a professional headshot, and about a dozen of casual photos…plus some follow up via email.
Photo of my ID? Casual photos?
Were they training an AI or something?
It wasn’t a “hell yes” anymore.&lt;/p&gt;

&lt;p&gt;A “hell yes” could consume more time than expected.
Or it couldn’t be a real “hell yes” after all.&lt;/p&gt;

&lt;p&gt;Choose what to focus on and say no.
Remember: a “hell yes” can always become a no.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Random But Interesting Ideas I Found Last Week</title>
   <link href="https://canro91.github.io/2026/05/02/RandomIdeas/"/>
   <updated>2026-05-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/02/RandomIdeas</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Recently, I’ve started to list interesting ideas I found over the week—to practice &lt;a href=&quot;/2025/06/17/TenIdeas/&quot;&gt;my 10-idea list habit&lt;/a&gt;. Here are 7 from the last week.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. The Matthew effect.&lt;/strong&gt;
Inspired by a verse from the gospel of Matthew, recognition goes to already-recognized people.
&lt;a href=&quot;/2026/01/19/LinkedIn/&quot;&gt;Visit social media&lt;/a&gt; and you’ll see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. The one sentencing to death wields the sword.&lt;/strong&gt;
Or something like that.
This comes from Game of Thrones.
Decision makers should stay close to their decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Don’t treat reading as background noise.&lt;/strong&gt;
Know whether you’re reading for pleasure and insight.
When reading for insight, &lt;a href=&quot;/2025/06/22/SimplerNoteTaking/&quot;&gt;make it active&lt;/a&gt;.
Otherwise, you won’t remember anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Solve the problem with what’s in your room.&lt;/strong&gt;
Or &lt;a href=&quot;/2026/04/28/YourRoomAndHead/&quot;&gt;in your head or in your notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Three interesting questions from Tim Ferriss’s Tools of Titans:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;What if you only solve a problem by removing something?&lt;/li&gt;
  &lt;li&gt;What if you only had &lt;a href=&quot;/2025/12/30/TwoHours/&quot;&gt;two hours to create&lt;/a&gt;?&lt;/li&gt;
  &lt;li&gt;What’s the least crowded place to go?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;#6. When writing anything emotional, &lt;a href=&quot;/2026/02/07/Handwriting/&quot;&gt;write by hand&lt;/a&gt;.&lt;/strong&gt;
For everything else, type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Start writing by the end.&lt;/strong&gt;
What’s the one thing you want readers to take away?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;Writing a 10-idea list changed my life&lt;/a&gt;. Try it! It could change yours too.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Leaving GitHub, Friendster revival, and life in 1999</title>
   <link href="https://canro91.github.io/2026/05/01/FridayLinks/"/>
   <updated>2026-05-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/05/01/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; GitHub made it to the headlines with outages and Copilot pricing. Mitchell Hashimoto, creator of Vagrant and Ghostty, is already &lt;a href=&quot;https://mitchellh.com/writing/ghostty-leaving-github&quot;&gt;leaving GitHub&lt;/a&gt; (4min). He’s not the only one. But believe it or not, there was still open source &lt;a href=&quot;https://lucumr.pocoo.org/2026/4/28/before-github/&quot;&gt;before GitHub&lt;/a&gt; (13min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; I never used it, but a guy is trying to &lt;a href=&quot;https://ca98am79.medium.com/i-bought-friendster-for-30k-heres-what-i-m-doing-with-it-d5e8ddb3991d&quot;&gt;revive Friendster&lt;/a&gt; (3min), the first social media network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; The defense industry already faced cheaper alternatives, efficiencies and consolidation, and a broken talent pipeline. Now &lt;a href=&quot;https://techtrenches.dev/p/the-west-forgot-how-to-make-things&quot;&gt;it’s time for the tech industry&lt;/a&gt; (15min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Want some time off from social media? What about &lt;a href=&quot;https://joshblais.com/blog/using-the-internet-like-its-1999/&quot;&gt;using the internet like it’s 1999&lt;/a&gt; (11min)? Not with a desperately slow connection, but without feeds.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;In case you missed it, last week I wrote about &lt;a href=&quot;/2026/04/21/Aggregators/&quot;&gt;a newspaper-style feed for blog aggregators&lt;/a&gt; (2min) and &lt;a href=&quot;/2026/04/25/Newspaper/&quot;&gt;Bubbles&lt;/a&gt; (2min) did it. Also, I documented &lt;a href=&quot;/2026/04/27/RandomIdeas/&quot;&gt;7 interesting ideas I found recently&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Street-Smart Coding&lt;/a&gt;, 30 lessons to help you code like a pro. From Googling to clear communication, it shares the lessons to help you stand out in the age of AI.&lt;/p&gt;

&lt;p&gt;See you next Friday with more links.&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coding For A Living Is More Performance Than Code</title>
   <link href="https://canro91.github.io/2026/04/30/SideQuest/"/>
   <updated>2026-04-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/30/SideQuest</id>
   <content type="html">&lt;p&gt;80% of the time, coding is a performance. The other 20% is doing real coding.&lt;/p&gt;

&lt;p&gt;Performance means Scrum ceremonies, meetings, and JIRA:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A meeting to present tickets for the next two weeks&lt;/li&gt;
  &lt;li&gt;A meeting to answer questions from the previous meeting&lt;/li&gt;
  &lt;li&gt;A meeting to watch someone enter a number in a text box. Read: poker planning and story points&lt;/li&gt;
  &lt;li&gt;Meetings to watch someone move a ticket between JIRA lanes. Read: daily meetings&lt;/li&gt;
  &lt;li&gt;A meeting about all the other meetings. Read: retrospective&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And &lt;a href=&quot;/2022/12/17/LessonsOnAFailedProject/&quot;&gt;when things go sideways&lt;/a&gt;, the performance intensifies with more frequent meetings. Sometimes a team member’s only job is running ceremonies and writing reports.&lt;/p&gt;

&lt;p&gt;That kills the fun of coding and can &lt;a href=&quot;/2026/04/29/Coding/&quot;&gt;make you hate it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This happens everywhere. Sylwia Laskowska asked in &lt;a href=&quot;https://dev.to/sylwia-lask/is-software-development-just-a-side-quest-a-jira-story-4ng3&quot;&gt;a dev.to post&lt;/a&gt; if that’s something that only happens at her place. Nope! Even in the best families, as we say in our hometown.&lt;/p&gt;

&lt;p&gt;Showing progress matters more than real work. Coding is often just a side quest. &lt;a href=&quot;/2025/11/20/HardTruths/&quot;&gt;A hard truth&lt;/a&gt; nobody tells us about.&lt;/p&gt;

&lt;p&gt;To succeed as a coder, you need to master the ceremonies as much as the code. That’s why &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=coding-living-is-performance-code&quot;&gt;Street-Smart Coding&lt;/a&gt; covers communication and collaboration. Because coding is more than typing symbols.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Love-Hate Letter to Coding</title>
   <link href="https://canro91.github.io/2026/04/29/Coding/"/>
   <updated>2026-04-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/29/Coding</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This is my submission for the &lt;a href=&quot;https://hamatti.org/posts/indieweb-carnival-write-a-love-letter/&quot;&gt;IndieWeb Carnival May 2026&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dear coding,&lt;/p&gt;

&lt;p&gt;Our story didn’t start with love at first sight. You weren’t my first option. Sorry! But when we started to hang out, everything clicked. There was chemistry!&lt;/p&gt;

&lt;p&gt;First, it was a textbook on C/C++ in college. Then, it was a recipe catalog in PHP late at night. Then, it was Java. Then, &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;our first adventure in the real world&lt;/a&gt; with C#. Then, building, fixing, growing—what others would call “passion.”&lt;/p&gt;

&lt;p&gt;I enjoyed the time we spent together. It was &lt;a href=&quot;/2025/03/18/ADayInTheLifeOfACoder/&quot;&gt;the challenge&lt;/a&gt;, the victory dances, the aha moments at the most unexpected times…It was funny, rewarding, almost magical.&lt;/p&gt;

&lt;p&gt;But something felt off. The passion faded, and then one day, everything changed.&lt;/p&gt;

&lt;p&gt;I don’t blame you. It was the corporate world, &lt;a href=&quot;/2024/12/11/SelfManagedTeam/&quot;&gt;Scrum and its ceremonies&lt;/a&gt;, unrealistic deadlines, office politics…They all ruined our thing. I stopped waking up eager for you. I didn’t want to talk or read about you. &lt;a href=&quot;/2026/02/22/Digestion/&quot;&gt;I got sick&lt;/a&gt;. &lt;a href=&quot;/2025/03/22/IsBurnoutInevitable/&quot;&gt;I got burned out&lt;/a&gt;. I got tired of us.&lt;/p&gt;

&lt;p&gt;Then, to make things worse, &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;a layoff set us apart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was a hard time. We stopped seeing each other. Nobody hired me to be with you again. And I stopped looking.&lt;/p&gt;

&lt;p&gt;It was a long time away. Honestly, I didn’t miss you. I thought we would never cross paths again. After months of applications, hope arrived in an unexpected email. Someone wanted us back together. But things can’t be the same after a breakup. We now need &lt;a href=&quot;/2025/03/03/Boundaries/&quot;&gt;boundaries&lt;/a&gt; and space.&lt;/p&gt;

&lt;p&gt;Coding, I sometimes hate you. I sometimes dream about a life without you. But you rewired my brain, making me see the world with different eyes, and that’s why I’ll always love you.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Solve the Problem With What&apos;s in Your Room—or Your Head</title>
   <link href="https://canro91.github.io/2026/04/28/YourRoomAndHead/"/>
   <updated>2026-04-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/28/YourRoomAndHead</id>
   <content type="html">&lt;p&gt;Constraints make you more creative.&lt;/p&gt;

&lt;p&gt;What better constraint than &lt;em&gt;“solving the problem with what’s in your room”&lt;/em&gt;?
That’s a line from Edwin H. Land, founder of Polaroid.
I knew about it from this &lt;a href=&quot;https://bookofjoe2.blogspot.com/2026/04/solve-problem-with-whats-in-your-head.html&quot;&gt;bookofjoe’s post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before he had his own lab, Edwin snuck into his university lab after hours.
Maybe that’s why he adopted that line.&lt;/p&gt;

&lt;p&gt;What about solving a problem with what’s in your head, library, and notes?
It forces recall and sparks connections.
It make us read and learn broadly.
It help us &lt;a href=&quot;/2024/12/16/FindYourPassion/&quot;&gt;build range&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s a good excuse to &lt;a href=&quot;/2026/04/14/Zettelkasten/&quot;&gt;revive my Zettelkasten&lt;/a&gt;—and a good challenge to &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;write a book&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Random But Curious And Interesting Ideas I Found Last Week</title>
   <link href="https://canro91.github.io/2026/04/27/RandomIdeas/"/>
   <updated>2026-04-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/27/RandomIdeas</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;#1. 100-repetition rule.&lt;/strong&gt; When starting a new hobby, aim for 100 repetitions to &lt;a href=&quot;/2025/01/02/SturgeonLaw/&quot;&gt;judging your progress&lt;/a&gt; or quitting. That’s 100 posts, 100 photos, or 100 paintings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. I feel/I wonder/I think.&lt;/strong&gt; &lt;a href=&quot;/2026/04/14/Zettelkasten/&quot;&gt;Reviving my Zettelkasten&lt;/a&gt; led me to &lt;a href=&quot;https://www.youtube.com/@morganeua&quot;&gt;morganeua’s YouTube channel&lt;/a&gt;. To react to something, answer I feel &lt;em&gt;__, I wonder __&lt;/em&gt;, and I think ___. Those answers will help you to create new notes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Cover design idea.&lt;/strong&gt; Last week, I found my copy of &lt;em&gt;The Ruthless Elimination of Hurry&lt;/em&gt; by John Mark Comer, and noticed its cover. The title is on the cover and its subtitle is on the back cover. Simple! I’m &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;stealing that idea&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Techno selectionism.&lt;/strong&gt; You don’t have to stick to a piece of tech. You can always try it, watch it, and change it. If it drains you or disconnects you, drop it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. No boredom, please.&lt;/strong&gt; We prefer to receive electric shocks rather than &lt;a href=&quot;/2026/02/03/Boredom/&quot;&gt;spend time alone with our thoughts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Work with your garage door open.&lt;/strong&gt; If marketing and sales sound daunting, just &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;show your progress&lt;/a&gt;. Open your garage door and let people see what you’re building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Read one book about work per year.&lt;/strong&gt; I haven’t read a coding book since &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;my burnout and layoff in 2024&lt;/a&gt;. This idea inspired me to pick one again. I opened my Books folder and started reading &lt;em&gt;Dependency Injection: Principles, Practices, and Patterns&lt;/em&gt; by Mark Seeman.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you’re a coder who wants to read something about work, you’ll find &lt;a href=&quot;/books&quot;&gt;my books here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Sunset, a Rooftop, and a Dream (A 100-Word Fiction Story Inspired By a Photo)</title>
   <link href="https://canro91.github.io/2026/04/26/Sunset/"/>
   <updated>2026-04-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/26/Sunset</id>
   <content type="html">&lt;p&gt;&lt;em&gt;A search for vignettes took me to a YouTube video by Bookfox on short stories, then to its blog, and finally to &lt;a href=&quot;https://100wordstory.org/photo-prompt/&quot;&gt;100 word story&lt;/a&gt;. Each month’s photo inspires a 100-word story. Here’s this month’s photo and my story.&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2026-04-26-Sunset/GameAtSunset.jpg&quot; alt=&quot;Game at sunset&quot; /&gt;
    &lt;figcaption&gt;Game at sunset. Photo by Orlando Contreras Lopes on flickr.com&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;“One day, I’ll be like Messi or Cristiano Ronaldo.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After school, the only thing he did was play street soccer.
No field.
Nets were two piles of stone.
His only audience was the next team waiting to challenge the winner.
In his mind, it was a gigantic stadium, people screaming “Gooaaal” as he took off his shirt.&lt;/p&gt;

&lt;p&gt;His class was full of doctors, police officers, and even a mayor.
He dreamed of Bernabéu, Maracaná, and the Champions League.&lt;/p&gt;

&lt;p&gt;But today, he only had an old ball to kick on a rooftop.
He only had a big dream and another sunset.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Bubbles Made It: A Newspaper-Style Feed</title>
   <link href="https://canro91.github.io/2026/04/25/Newspaper/"/>
   <updated>2026-04-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/25/Newspaper</id>
   <content type="html">&lt;p&gt;I manifested it…and Bubbles did it.&lt;/p&gt;

&lt;p&gt;Earlier this week, I wrote &lt;a href=&quot;/2026/04/21/Aggregators/&quot;&gt;blog aggregators should be like newspapers&lt;/a&gt;.
I found out about a new aggregator, &lt;a href=&quot;https://bubbles.town/&quot;&gt;Bubbles&lt;/a&gt;, and gave it a try.&lt;/p&gt;

&lt;p&gt;I realized “new” and “random” views are just like feeds.
They’re fun the first time, especially “random,” until &lt;a href=&quot;/2026/02/24/NoSocialMediaWeek/&quot;&gt;you’re hooked&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With a newspaper, there’s no scrolling.
Read it and wait for the next day.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dshbx.de/blog/bubbles/entry/722acd4a-266a-4de6-93d7-4c10a17807cb&quot;&gt;Bubbles did exactly that&lt;/a&gt;.
Now it has a “Briefing,” yesterday’s best stories, like a newspaper.
Perfect to keep reducing &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;your phone time&lt;/a&gt;.
The best part, it greets you with &lt;em&gt;“Go outside. Touch some grass”&lt;/em&gt; when you reach the end.&lt;/p&gt;

&lt;p&gt;Thanks Ben for making it a reality.&lt;/p&gt;

&lt;p&gt;Now my daily reading is a dose of Hacker News, Minifeed, and Bubbles Briefing.&lt;/p&gt;

&lt;p&gt;I didn’t know I had magic powers.
Turns out, it’s not magic, it’s being &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;an idea machine&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Three Tweaks I&apos;ve Made To My Blog For a Fresh Start</title>
   <link href="https://canro91.github.io/2026/04/24/Tweaks/"/>
   <updated>2026-04-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/24/Tweaks</id>
   <content type="html">&lt;p&gt;Back in March, I wrote a list of &lt;a href=&quot;/2026/03/03/Tweaks/&quot;&gt;changes I wanted to make to my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To hold myself accountable, here are the changes I’ve made:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Scrollable sidebar.&lt;/strong&gt; The original &lt;a href=&quot;https://github.com/poole/hyde&quot;&gt;Hyde theme&lt;/a&gt; had a sticky sidebar. But after adding more items, it overflowed off the screen. Now it’s scrollable. I can add as many items as I want. Muahahaha!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. New tagline.&lt;/strong&gt; After &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;burning out&lt;/a&gt; and &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;diversifying my joy&lt;/a&gt;, I stopped being a “passionate” coder. These days, I’m a lifelong learner who codes part-time and &lt;a href=&quot;/2026/04/19/WhyIWrite/&quot;&gt;write most of the time&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s why I changed my tagline on the sidebar from simply &lt;em&gt;“Software engineer”&lt;/em&gt; to &lt;em&gt;“Sometimes I code, always I write.”&lt;/em&gt; Cooler, isn’t it? And it reflects what you will find here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Better homepage and about copy.&lt;/strong&gt; My blog isn’t a coding-only blog anymore. It’s more like &lt;a href=&quot;/2026/03/23/TimeCapsule/&quot;&gt;a time capsule&lt;/a&gt;. My &lt;a href=&quot;/about&quot;&gt;About page&lt;/a&gt; reflects that now…and I had some typos. So embarrassing, &lt;a href=&quot;/2025/01/23/CallingYourselfAWriter/&quot;&gt;calling myself a writer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And on my homepage, I’m only featuring &lt;a href=&quot;/blog&quot;&gt;my blog&lt;/a&gt; and &lt;a href=&quot;/books&quot;&gt;my books&lt;/a&gt;. &lt;a href=&quot;/2025/07/07/AskingForCoffee/&quot;&gt;No more Buy Me a Coffee&lt;/a&gt; and Udemy courses. Less clutter.&lt;/p&gt;

&lt;p&gt;That’s the good part of personal blogs—and the bad part too. You’re never done tweaking it. And if &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;you code and want to blog&lt;/a&gt;, don’t start by writing a blogging engine. Wait, did I say that out loud?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Agile guilty, software laws, and Postman pains</title>
   <link href="https://canro91.github.io/2026/04/24/FridayLinks/"/>
   <updated>2026-04-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/24/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; I have many horror stories from daily meetings and other “ceremonies.” From SCRUM masters being police officers to sticky notes signed with blood. &lt;a href=&quot;https://blog.rpanachi.com/how-agile-killed-the-software-industry&quot;&gt;Agile killed the software industry&lt;/a&gt; (6min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Conway’s law, Brooks’ law, Peter principle…Do they ring a bell? Well, those are some of &lt;a href=&quot;https://lawsofsoftwareengineering.com/&quot;&gt;the laws of software engineering&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; At a past job, Postman was our official API tool. Then it went paid, then it became…something else? For something as simple as calling APIs, &lt;a href=&quot;https://efp.asia/blog/2025/12/24/api-tooling-crisis/&quot;&gt;we’re in a tooling crisis&lt;/a&gt; (7min). Maybe all we need is &lt;a href=&quot;/2025/05/14/AlternativeToPostman/&quot;&gt;curl&lt;/a&gt; (2min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Have you ever used a typewriter? Imagine writing an essay with no AI, spellchecker, or backspace. That’s what &lt;a href=&quot;https://sentinelcolorado.com/uncategorized/a-college-instructor-turns-to-typewriters-to-curb-ai-written-work-and-teach-life-lessons/&quot;&gt;this teacher asks his students to use&lt;/a&gt; (5min) once each semester.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/04/20/TheOldWay/&quot;&gt;the main advantage of coding the old way&lt;/a&gt; (2min). Yes, not using AI is already “the old way.”&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Street-Smart Coding&lt;/a&gt;, 30 lessons to help you code like a pro. From Googling to clear communication, it shares the lessons to help you stand out in the age of AI.&lt;/p&gt;

&lt;p&gt;See you next Friday with more links.&lt;/p&gt;

&lt;p&gt;Keep coding the old way (or not).&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Life Lesson Learned From Dressing Up at the Airport</title>
   <link href="https://canro91.github.io/2026/04/22/Appearances/"/>
   <updated>2026-04-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/22/Appearances</id>
   <content type="html">&lt;p&gt;A 2-hour flight to the capital city for an early morning meeting at a government office…&lt;/p&gt;

&lt;p&gt;I showed up late to the airport booth. Passengers were already getting on.&lt;/p&gt;

&lt;p&gt;I was working for a small tech shop in my city. I wasn’t a manager, but the CEO chose me to fly with one to represent our company.&lt;/p&gt;

&lt;p&gt;My dad offered to drive me. Before rush hour, the airport was just 15 minutes away. But that day, the car wouldn’t start. Each time he turned the key, the engine just made funny noises and die. Arrggg!&lt;/p&gt;

&lt;p&gt;In the airport booth, I saw an empty line at the security control. My coworker was texting me. He was already onboarding. I had no other choice but to plead guilty.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Good morning. I know I’m late. The car I was in broke down. My assistant was supposed to send me my boarding pass but I can’t find it in my email. I have a meeting with the government at 8:00 a.m.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I didn’t really have an assistant. I meant the office manager, who helped us all. I lied. I know that’s bad. I didn’t want to pay for a ticket with my own money. But the part of accessing my email was true.&lt;/p&gt;

&lt;p&gt;I was well-shaved, wearing a long-sleeve shirt, and holding a jacket. &lt;em&gt;“Do you only have hand luggage?”&lt;/em&gt; I was carrying a small backpack. She took the radio, called the security line, and let me in.&lt;/p&gt;

&lt;p&gt;Behind me was a vacationing family. You know, shorts, t-shirts, and flip flops. They were also late and also on the same flight.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“It’s too late. Flight is closed,”&lt;/em&gt; they were told.&lt;/p&gt;

&lt;p&gt;Like it or not, we judge and are judged by how we dress and speak. And if you’re late, always admit it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Simple Feature That Could Transform IndieWeb Aggregators</title>
   <link href="https://canro91.github.io/2026/04/21/Aggregators/"/>
   <updated>2026-04-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/21/Aggregators</id>
   <content type="html">&lt;p&gt;While checking my blog stats today, I found a surprising new source of traffic.&lt;/p&gt;

&lt;p&gt;Apart from the usual search engines, I found Hacker News, Kagi, Minifeed, and Bubbles.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/03/27/IndieWebAndMinifeed/&quot;&gt;I knew about Minifeed&lt;/a&gt;. But &lt;a href=&quot;https://bubbles.town/about&quot;&gt;Bubbles&lt;/a&gt; was new. It’s like Hacker News meets RSS for IndieWeb. They both solve &lt;a href=&quot;/2025/01/27/TimesOfDeadBlogging/&quot;&gt;the discoverability problem of blogs&lt;/a&gt;. More power to personal blogs!&lt;/p&gt;

&lt;p&gt;But blog aggregators should be like newspapers.&lt;/p&gt;

&lt;p&gt;When you open one, you’ll only find news from yesterday, or previous days if still relevant. You’ll have to wait until tomorrow to read what happened today.&lt;/p&gt;

&lt;p&gt;Aggregator views (like latest, top, and random) often invite &lt;a href=&quot;/2026/02/24/NoSocialMediaWeek/&quot;&gt;endless scrolling&lt;/a&gt;, like in social media. I want to know when I’m done with the IndieWeb each day. What about “today” and “yesterday” views? Or maybe I’m too lazy to check post dates in the feed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you found this post through Minifeed, Bubbles, or another aggregator, welcome! On &lt;a href=&quot;/about&quot;&gt;my About&lt;/a&gt; page, you’ll find my most popular posts. Feel free to check &lt;a href=&quot;/books&quot;&gt;my books&lt;/a&gt; on coding and personal development.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Main Advantage Of Coding &quot;The Old Way&quot; Over AI-Assisted Coding</title>
   <link href="https://canro91.github.io/2026/04/20/TheOldWay/"/>
   <updated>2026-04-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/20/TheOldWay</id>
   <content type="html">&lt;p&gt;Typing out code is already the old way.&lt;/p&gt;

&lt;p&gt;Last week, I stumbled upon &lt;a href=&quot;https://miguelconner.substack.com/p/im-coding-by-hand&quot;&gt;I’m Coding By Hand&lt;/a&gt;. Its subtitle got my attention: &lt;em&gt;“I’m spending 3 months coding the old way.”&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-old-way-of-coding&quot;&gt;The old way of coding&lt;/h2&gt;

&lt;p&gt;The future of coding is already here.&lt;/p&gt;

&lt;p&gt;Decades ago, we punched cards.
For so long, we’ve been &lt;a href=&quot;/2020/09/14/LearnVimForFunAndProfit/&quot;&gt;typing out symbols inside IDEs&lt;/a&gt;.
That’s now “the old way.”&lt;/p&gt;

&lt;p&gt;Maybe the future is AI specs via brain implants. Who knows!&lt;/p&gt;

&lt;h2 id=&quot;what-you-miss-when-coding-with-ai&quot;&gt;What you miss when coding with AI&lt;/h2&gt;

&lt;p&gt;The post made an excellent distinction between old coding vs AI-assisted coding.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“when writing code ‘by hand’ I was actually doing two things: writing what I wanted and learning the codebase…if I didn’t know what I wanted exactly, coding agents would be happy to make many assumptions for me. This almost always meant that I didn’t learn as much, and that I wouldn’t have a good grasp of the codebase.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Coding isn’t just typing.&lt;/p&gt;

&lt;p&gt;It’s decoding business rules or problem constraints to then encode them into a programming language, while&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Mastering its syntax and rules,&lt;/li&gt;
  &lt;li&gt;Making the new coding artifacts interact with existing ones,&lt;/li&gt;
  &lt;li&gt;Following the structure and conventions of the codebase,&lt;/li&gt;
  &lt;li&gt;Learning about the business domain,&lt;/li&gt;
  &lt;li&gt;And of course, solving the problem at hand.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Maybe with AI, we may lose some of those activities.
If AI writes 90% of code, &lt;a href=&quot;/2026/01/08/FewerLanguages/&quot;&gt;being a polyglot isn’t valuable&lt;/a&gt; anymore.&lt;/p&gt;

&lt;p&gt;In the meantime, every AI-generated line of code we don’t understand isn’t just technical debt. It’s cognitive debt.&lt;/p&gt;

&lt;p&gt;AI is like a powerful calculator: only useful if you know the math. To help you level up your coding skills—AI-assisted coding or old school—check out &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=main-advantage-of-coding-old-way-aiassisted-coding&quot;&gt;Street-Smart Coding&lt;/a&gt;. The roadmap I wish I had starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Nobel Prize–Winning Writer Made Me Ask Why I Write</title>
   <link href="https://canro91.github.io/2026/04/19/WhyIWrite/"/>
   <updated>2026-04-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/19/WhyIWrite</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Today I found a short version of &lt;a href=&quot;https://www.nobelprize.org/prizes/literature/2006/pamuk/lecture/&quot;&gt;Orhan Pamuk’s Nobel acceptance speech&lt;/a&gt;. His words about why he writes inspired me to write my own reply. Here’s why I write.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I write because I want to be sane.&lt;/p&gt;

&lt;p&gt;I write because I don’t want to &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;burn out again&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I write because I want to bring order to my mind.&lt;/p&gt;

&lt;p&gt;I write because I enjoy being alone with my thoughts.&lt;/p&gt;

&lt;p&gt;I write because I like to be in the zone.&lt;/p&gt;

&lt;p&gt;I write because I want to &lt;a href=&quot;/2025/04/16/WritingChanges/&quot;&gt;get better at it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;* * *&lt;/p&gt;

&lt;p&gt;I write because sometimes I can’t be quiet.&lt;/p&gt;

&lt;p&gt;I write because other times I want to stop talking.&lt;/p&gt;

&lt;p&gt;* * *&lt;/p&gt;

&lt;p&gt;I write because I want to share my life’s lessons.&lt;/p&gt;

&lt;p&gt;I write because I want to help and inspire.&lt;/p&gt;

&lt;p&gt;I write because people already ask me about &lt;a href=&quot;/books&quot;&gt;my first books&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;* * *&lt;/p&gt;

&lt;p&gt;I write because I want &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;my future kids to read my books&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I write because one day I’d like to see my books in bookstores.&lt;/p&gt;

&lt;p&gt;I write because one day I’d like to be someone’s favorite writer.&lt;/p&gt;

&lt;p&gt;* * *&lt;/p&gt;

&lt;p&gt;And finally…&lt;/p&gt;

&lt;p&gt;I write because I don’t want to go back to a 9-to-5 again.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why You Shouldn&apos;t Be Afraid of Rereading Books</title>
   <link href="https://canro91.github.io/2026/04/18/Rereading/"/>
   <updated>2026-04-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/18/Rereading</id>
   <content type="html">&lt;p&gt;I read Deep Work in August 2020.&lt;/p&gt;

&lt;p&gt;I know because &lt;a href=&quot;/2024/05/13/HowToReadNonFictionBooks/&quot;&gt;I took notes&lt;/a&gt;.
I have in &lt;a href=&quot;/2026/04/14/Zettelkasten/&quot;&gt;my digital slip-box&lt;/a&gt; excerpts and reactions.
I only remembered the &lt;em&gt;Monk Mode:&lt;/em&gt; isolation for a period to focus on a project.&lt;/p&gt;

&lt;p&gt;Recently, my mission to &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;reduce my phone time&lt;/a&gt; brought the book back to my attention.&lt;/p&gt;

&lt;p&gt;That’s when I pulled out my notes and found all the quotes and excerpts.
This time, reading it would have more meaning.
It’s no longer about &lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;hitting a book count&lt;/a&gt;.
I have a running project.
A concrete goal.&lt;/p&gt;

&lt;p&gt;The book hasn’t change.
But I have.
That makes the reading different.&lt;/p&gt;

&lt;p&gt;And that’s why you should &lt;a href=&quot;/2026/03/09/ReadingRules/&quot;&gt;reread books&lt;/a&gt; too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: AI adoption, cheap stack, and obfuscation</title>
   <link href="https://canro91.github.io/2026/04/17/FridayLinks/"/>
   <updated>2026-04-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/17/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; If AI is so good, why it hasn’t changed everything yet? Here’s &lt;a href=&quot;https://davegriffith.substack.com/p/why-isnt-everything-different-yet&quot;&gt;a checklist for a new tech to transform anything&lt;/a&gt; (10min). I liked the item about judging when not to use a tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Often we don’t need Kubernetes, replication, and expensive databases. Here’s &lt;a href=&quot;https://stevehanov.ca/blog/how-i-run-multiple-10k-mrr-companies-on-a-20month-tech-stack&quot;&gt;a simple $20/month tech stack&lt;/a&gt; (9min) to experiment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Here are &lt;a href=&quot;https://spencermortensen.com/articles/email-obfuscation/&quot;&gt;the best techniques to protect emails from spammers&lt;/a&gt; (18min). Something to bookmark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; I’m in the &lt;a href=&quot;https://www.0xsid.com/blog/wont-download-your-app&quot;&gt;I won’t install your app&lt;/a&gt; (5min) team.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it… This week, living through &lt;a href=&quot;https://canro91.github.io/2026/03/28/ExcelParadox/&quot;&gt;the Excel paradox of coding&lt;/a&gt; (1min), I found myself dealing with &lt;a href=&quot;https://canro91.github.io/2026/04/10/InvalidColumns/&quot;&gt;CSV files in C# using CsvHelper&lt;/a&gt; (5min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Street-Smart Coding&lt;/a&gt;, 30 lessons to help you code like a pro. From Googling to clear communication, it shares the lessons to help you stand out in the age of AI.&lt;/p&gt;

&lt;p&gt;See you next Friday with more links.&lt;/p&gt;

&lt;p&gt;Keep coding smartly.&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>From Skepticism to Overreliance, My Journey With AI (And How to Thrive as a Coder)</title>
   <link href="https://canro91.github.io/2026/04/16/Journey/"/>
   <updated>2026-04-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/16/Journey</id>
   <content type="html">&lt;h2 id=&quot;nah-i-dont-care&quot;&gt;Nah… I don’t care&lt;/h2&gt;

&lt;p&gt;In early 2023, a coworker tried that new thing called ChatGPT.
He shared his excitement.
I wasn’t that impressed.&lt;/p&gt;

&lt;p&gt;Months later, after all the buzz, I searched that &lt;em&gt;Chat thing&lt;/em&gt;.
Every time I tried, I couldn’t access it.
It was a public beta or something.
Too many people.&lt;/p&gt;

&lt;p&gt;Then, I learned from &lt;a href=&quot;/2025/02/02/LessonsFromBrentOzar/&quot;&gt;Brent Ozar&lt;/a&gt; to keep a browser tab with ChatGPT open as a junior assistant.
&lt;em&gt;Mmm, there’s something there…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In early 2024, &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;Devin was released&lt;/a&gt;.
“The sky is falling” all over the headlines.
I didn’t buy it.&lt;/p&gt;

&lt;h2 id=&quot;omg-this-is-dangerous&quot;&gt;OMG! This is dangerous!&lt;/h2&gt;

&lt;p&gt;Early 2025, I decided to try AI with a fresh mind.&lt;/p&gt;

&lt;p&gt;After using Copilot for weeks, &lt;em&gt;“OMG! This thing is dangerous.”&lt;/em&gt;
One day I realized I couldn’t finish a simple task alone.
The tool worked, but &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;I was becoming so dependent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An AI detox season followed.
&lt;em&gt;No more AI until you eat your vegetables and do your homework.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then I made &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;/2026/01/26/AnotherAIRule/&quot;&gt;rules&lt;/a&gt; to use AI without losing my skills.&lt;/p&gt;

&lt;p&gt;Like any other tool, AI is a game changer with the right skills.
Otherwise, &lt;a href=&quot;/2025/11/27/HowToThinkOfAI/&quot;&gt;it’s like a fancy calculator&lt;/a&gt; in a math exam you didn’t study for.&lt;/p&gt;

&lt;p&gt;To thrive in the AI era, &lt;a href=&quot;/2026/02/12/RealSkills/&quot;&gt;build real skills, then leverage AI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To help you build AI-proof skills, I wrote &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=my-new-book-is-here&quot;&gt;Street-Smart Coding&lt;/a&gt;. The guide I wish I had on my journey from junior to senior.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Healthcare Provider Dropped This Doctor—He Has 2 Specialties And Over 40 Years of Experience</title>
   <link href="https://canro91.github.io/2026/04/15/Age/"/>
   <updated>2026-04-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/15/Age</id>
   <content type="html">&lt;p&gt;This time, the waiting room was almost empty.&lt;/p&gt;

&lt;p&gt;I went with my sister to her dermatologist.
His office is across town near the beach.
We walk around afterwards.
Each visit feels like a day off.&lt;/p&gt;

&lt;p&gt;After going to the same doctor for years, my sister chats casually with him like old friends.&lt;/p&gt;

&lt;p&gt;She asked him if there was another doctor in the area.
The waiting room wasn’t crowded, and his schedule had plenty of openings.&lt;/p&gt;

&lt;p&gt;It turned out some healthcare providers let him go simply because of his age.
Some patients asked for a younger doctor.
My sister’s doctor is 72.&lt;/p&gt;

&lt;p&gt;They don’t know about &lt;a href=&quot;/2025/05/06/SharpMind/&quot;&gt;the oldest practicing doctor&lt;/a&gt; and his Guinness record.
Spoiler alert: he’s 102.&lt;/p&gt;

&lt;p&gt;My sister’s doctor walks without assistance, works from 8:00 to 7:00, has 2 specialties, performs surgeries, and if you see him around, you’d think he’s in his early 60s.
I saw him in the waiting room taking a coffee to his assistant.
He has seen countless patients and cases.&lt;/p&gt;

&lt;p&gt;My sister says he’s the best dermatologist she’s ever had.&lt;/p&gt;

&lt;p&gt;Often you don’t need newer, brighter, and faster, but &lt;a href=&quot;/2025/08/24/ExaminationRoom/&quot;&gt;more trustworthy&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;ve Been Following the Zettelkasten Method the Wrong Way—I&apos;m Fixing It</title>
   <link href="https://canro91.github.io/2026/04/14/Zettelkasten/"/>
   <updated>2026-04-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/14/Zettelkasten</id>
   <content type="html">&lt;p&gt;In 2020, I discovered the Zettelkasten method, and it transformed my note-taking.&lt;/p&gt;

&lt;p&gt;Back then, I read &lt;a href=&quot;/2020/11/18/HowToTakeSmartNotes/&quot;&gt;How to Take Smart Notes&lt;/a&gt; and started &lt;a href=&quot;/2024/05/13/HowToReadNonFictionBooks/&quot;&gt;my “perverted” digital version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently, to &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;stay away from screens&lt;/a&gt;, I decided to give Zettelkasten another try.&lt;/p&gt;

&lt;p&gt;A rabbit hole led me to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://zettelkasten.de/communications-with-zettelkastens/&quot;&gt;a Luhmann’s essay&lt;/a&gt;, showing how to use bibliographical or literature notes.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.semanticscholar.org/paper/Niklas-Luhmann%E2%80%99s-Card-Index%3A-Thinking-Tool%2C-Machine-Schmidt/7755fed320c4bb1e3517a18abf0b7791e35375bb&quot;&gt;a paper on his system&lt;/a&gt;, with photos of Luhmann’s notes as examples.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://writing.bobdoto.computer/&quot;&gt;a helpful blog&lt;/a&gt;, clarifying what to put into a main or permanent note.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what I learned to revive my Zettelkasten with pen and paper:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. The goal isn’t to take notes, but to write.&lt;/strong&gt;
Read while keeping your existing notes and projects in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Read with pen and paper, and keep a bibliographical note.&lt;/strong&gt;
Write page numbers, timestamps, and keywords.
Make it a personal index for a book or resource.&lt;/p&gt;

&lt;p&gt;When reading a book, Luhmann wrote its bibliographical information on one side of a card and on the other side, page numbers and keywords.
He kept one or two cards per book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Don’t write excerpts or quotes in main notes.&lt;/strong&gt;
Add your interpretations of what you consume.
If it triggers a thought or an aha moment, make it a main note.
Optionally, reference the bibliographical note that sparked it.&lt;/p&gt;

&lt;p&gt;In my perverted version, I kept bibliographical and main notes in one file.
I put my interpretations in the top half, instead of using a main note.&lt;/p&gt;

&lt;p&gt;Strictly speaking, I was only taking bibliographical notes.
I was keeping a commonplace book, not a Zettelkasten.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Link between a new note and existing notes.&lt;/strong&gt;
Think of hyperlinks between notes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Create an index card for keywords and subjects.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even without following the method to the letter, using separate pieces of paper is simple but effective to organize and connect ideas. Useful when outlining presentations or books.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Lessons To Run a Successful Business from Derek Sivers&apos; Anything You Want</title>
   <link href="https://canro91.github.io/2026/04/14/AnythingYouWant/"/>
   <updated>2026-04-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/14/AnythingYouWant</id>
   <content type="html">&lt;p&gt;What started as a hobby became a millionaire business.&lt;/p&gt;

&lt;p&gt;To sell his own music, Derek created a website and called it CD Baby. His friends asked him to include their CDs too. Years later, he sold it for millions.&lt;/p&gt;

&lt;p&gt;To stop answering the same questions, he wrote Anything You Want. You could notice &lt;a href=&quot;/2026/04/04/DerekSivers/&quot;&gt;his take on succint writing and short books&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are 10 lessons I learned from reading it—I’m practicing &lt;a href=&quot;/2025/06/22/SimplerNoteTaking/&quot;&gt;the 10-idea list habit&lt;/a&gt; and &lt;a href=&quot;/2026/01/25/7Words/&quot;&gt;the 7-word summary&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Your business is your utopia.&lt;/strong&gt; It’s your world where you make all the rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. You don’t have to make it look corporate.&lt;/strong&gt; Forget about a privacy policy and terms and conditions. It’s your world. Make it anything you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Share something people want and charge for it.&lt;/strong&gt; Starting &lt;a href=&quot;/2026/03/20/Entrepreneurship/&quot;&gt;a business doesn’t have to be complicated&lt;/a&gt; after all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. You don’t have to grow big, big.&lt;/strong&gt; It isn’t about growth, but being happy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Have a simple business model.&lt;/strong&gt; CD Baby charged an onboarding fee and another one per CD sold. Derek stole his business model from another publisher.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Get to know everyone who contacts you.&lt;/strong&gt; Your best clients are your current clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Always think about your clients.&lt;/strong&gt; Make them happy, even &lt;a href=&quot;/2026/04/12/Pizzas/&quot;&gt;when asking for favors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Every confusing word makes you lose money.&lt;/strong&gt; That applies for your website, emails, and every piece of copy. &lt;a href=&quot;/2025/02/10/SellingWithoutCalls/&quot;&gt;Rejecting sales calls&lt;/a&gt; makes you clarify your message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. If if doesn’t excite you anymore, move on.&lt;/strong&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why I Don&apos;t Do Sales Calls (On Either Side)</title>
   <link href="https://canro91.github.io/2026/04/12/SalesCalls/"/>
   <updated>2026-04-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/12/SalesCalls</id>
   <content type="html">&lt;p&gt;This was supposed to be a call to get some help.&lt;/p&gt;

&lt;p&gt;At least, that’s what I was told. But it turned out to be something else.&lt;/p&gt;

&lt;h2 id=&quot;an-invitation-to-a-call&quot;&gt;An invitation to a call…&lt;/h2&gt;

&lt;p&gt;This happened in the days I started to &lt;a href=&quot;/2025/04/30/WritingMyths/&quot;&gt;write seriously online&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After months of inactivity, I joined a social media platform.
I participated in the community, &lt;a href=&quot;/2025/08/05/LinkedInStrategy/&quot;&gt;liking and commenting&lt;/a&gt; under big and small accounts.&lt;/p&gt;

&lt;p&gt;For weeks, I replied to almost every post from a big creator.
His message resonated with me.
He left his regular job to &lt;a href=&quot;/2025/01/23/CallingYourselfAWriter/&quot;&gt;make a living writing&lt;/a&gt;.
My dream!&lt;/p&gt;

&lt;p&gt;One day, he messaged me asking me about my goals.
He offered to join a free call to see if he could help.&lt;/p&gt;

&lt;h2 id=&quot;the-day-of-the-call&quot;&gt;The day of the call…&lt;/h2&gt;

&lt;p&gt;Somebody else showed up.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Definitely, we could help,”&lt;/em&gt; the guy said after asking why I was interested in writing.&lt;/p&gt;

&lt;p&gt;He showed me all the incredible benefits of joining their writing program.
Testimonials, weekly coaching calls, a vibrant community…
You name it!&lt;/p&gt;

&lt;p&gt;When I asked about the price, &lt;em&gt;“Forget about the price. What would your life look if you achieve this goal?”&lt;/em&gt;
They promised hitting $10K a month.&lt;/p&gt;

&lt;p&gt;I hesitated and kept asking for prices.&lt;/p&gt;

&lt;p&gt;Then he typed some numbers. &lt;em&gt;“Here’s the full price… If you’re an action taker, here’s the price… Or you can pay in X installments of Y…“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wow! Far outside my budget. Then I knew why he asked about my income.&lt;/p&gt;

&lt;p&gt;There was some awkward silence, followed by the same question, &lt;em&gt;“What impedes you to take action now?”&lt;/em&gt;
Then, more silence, more awkward this time.&lt;/p&gt;

&lt;h2 id=&quot;being-an-action-taker&quot;&gt;Being an action taker…&lt;/h2&gt;

&lt;p&gt;After some back and forth, he created a lite version of the program for a discounted price.&lt;/p&gt;

&lt;p&gt;That was “all” he could do for me.&lt;/p&gt;

&lt;p&gt;But there was a catch, I had to pay within the next 24 hours.
Otherwise, I’d prove I wasn’t &lt;em&gt;“an action taker committed to achieving my goals”&lt;/em&gt; or something.&lt;/p&gt;

&lt;p&gt;I sent the receipt to the creator who started the conversation.&lt;/p&gt;

&lt;p&gt;Radio silence.&lt;/p&gt;

&lt;p&gt;Each day made me worry more.
&lt;em&gt;“How can I be so stupid?”&lt;/em&gt;
I checked my card activity, hoping not to see anything suspicious.&lt;/p&gt;

&lt;p&gt;After a day or two, I got a link to join the course and the community.&lt;/p&gt;

&lt;p&gt;It was some video recordings.
Not that bad, but overpriced.&lt;/p&gt;

&lt;p&gt;I definitely felt tricked. A hand crossed the internet, through my screen, into my pocket.&lt;/p&gt;

&lt;h2 id=&quot;hey-ive-seen-this-one&quot;&gt;“Hey, I’ve seen this one…”&lt;/h2&gt;

&lt;p&gt;More recently, the first email from a newsletter had a link to a webinar.&lt;/p&gt;

&lt;p&gt;The guy promised 8 tips his mentor taught him to live a “dream life.”
He claimed he works one day a month.
But he only shared one tip.&lt;/p&gt;

&lt;p&gt;To get the others, we only had until next Thursday midnight.
It was a “let me hold your hand” program, but you had to join a call to see if you were a good fit.
The price wasn’t disclosed anywhere.&lt;/p&gt;

&lt;p&gt;I unsubscribed.
&lt;em&gt;“Hey, I’ve seen this one, I’ve seen this one. It’s a classic,”&lt;/em&gt; Marty McFly would say.&lt;/p&gt;

&lt;p&gt;That wasn’t the first time I heard that story.&lt;/p&gt;

&lt;h2 id=&quot;if-i-dont-see-this&quot;&gt;If I don’t see this…&lt;/h2&gt;

&lt;p&gt;For a moment, I thought that call was a scam.&lt;/p&gt;

&lt;p&gt;The closer followed all the sales tactics.
It was a sales call to showcase in a textbook.
Finding a pain, painting a dream outcome, making a limited-time offer…&lt;/p&gt;

&lt;p&gt;I guess scammers and salespeople read the same books.
Or scammers are salespeople who joined the dark side.&lt;/p&gt;

&lt;p&gt;That call and the other webinar made me set a rule:
&lt;em&gt;If there’s no price, no time to decide, and the “help” comes wrapped in a call, I walk away without thinking twice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If “help” is just a hand reaching into your pocket, &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;it isn’t help at all&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s why &lt;a href=&quot;/2025/02/10/SellingWithoutCalls/&quot;&gt;I don’t do sales calls&lt;/a&gt;—on either side.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Follow This Proven Strategy to End Scope Creep (While Keeping Clients Happy)</title>
   <link href="https://canro91.github.io/2026/04/12/Pizzas/"/>
   <updated>2026-04-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/12/Pizzas</id>
   <content type="html">&lt;p&gt;There’s a thin line between making your clients happy and doing everything they want.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2026/04/04/DerekSivers/&quot;&gt;Derek Sivers&lt;/a&gt; in &lt;a href=&quot;/2026/04/14/AnythingYouWant/&quot;&gt;Anything You Want&lt;/a&gt; shares his strategy to end “small favors” while keeping clients happy.&lt;/p&gt;

&lt;p&gt;When running CD Baby, he had a clear task: create a sales page for your CD.
For anything else, he answered &lt;em&gt;“If you buy us a pizza, we’d do any favor you want.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A pizza isn’t free but generates small friction to end next favors.&lt;/p&gt;

&lt;p&gt;Replace pizzas with coffee or &lt;a href=&quot;/2024/12/11/SelfManagedTeam/&quot;&gt;hamburgers&lt;/a&gt;. Also great idea to also support local businesses.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to Read Invalid Fields as Null with CsvHelper</title>
   <link href="https://canro91.github.io/2026/04/10/InvalidColumns/"/>
   <updated>2026-04-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/10/InvalidColumns</id>
   <content type="html">&lt;p&gt;&lt;em&gt;In another episode of &lt;a href=&quot;/2026/03/28/ExcelParadox/&quot;&gt;the Excel paradox of Coding&lt;/a&gt;…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: To read an invalid field from a CSV file as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; with CsvHelper, create a map class and use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Default()&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useOnConversionFailure&lt;/code&gt; set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; on the appropriate field.&lt;/p&gt;

&lt;h2 id=&quot;the-cleanest-approach&quot;&gt;The cleanest approach&lt;/h2&gt;

&lt;p&gt;To honor &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;the 20-minute rule&lt;/a&gt; and &lt;a href=&quot;/2024/11/09/LimitedKeystrokes/&quot;&gt;preserve my keystrokes&lt;/a&gt;, let’s say we need to read a CSV file with CsvHelper. By default, it throws an exception when it fails to parse a column, for example when it finds text on a numeric column.&lt;/p&gt;

&lt;p&gt;Here’s how to read that invalid field as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; instead of throwing an exception,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CsvHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CsvHelper.Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Globalization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TestProject1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CsvHelperTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieRating&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieRatingMap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClassMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieRatingMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?),&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;useOnConversionFailure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// If the field is invalid, use the default value&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullWhenInvalidFloats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Name,Rating&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Inception,9.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic,ThisIsNotAValidRating&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;cm&quot;&gt;/* &amp;lt;-- */&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CsvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterClassMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieRatingMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetRecords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Look, ma! It&apos;s null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The magic happens in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MovieRatingMap&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rating&lt;/code&gt; map uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Default()&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useOnConversionFailure&lt;/code&gt; set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-manual-approach&quot;&gt;The manual approach&lt;/h2&gt;

&lt;p&gt;As an alternative, let’s read the CSV file manually while processing the records as needed,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ByHand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Name,Rating&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Inception,9.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic,ThisIsNotAValidRating&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CsvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterClassMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieRatingMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//        ^^^^^&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Open the reader and read the header&lt;/span&gt;

	&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Read it by hand...&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Rating&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Read the field as string...&lt;/span&gt;

		&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsedRating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NumberStyles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
								&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt;
								&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Parse it as needed...&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieRating&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsedRating&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Look, ma! It&apos;s still null&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A lot more work! We have full control, but it’s less clean.&lt;/p&gt;

&lt;p&gt;In case you’re wondering, &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;I asked Copilot&lt;/a&gt; about this but, with a straight face, it hallucinated suggesting a flag on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CsvConfiguration&lt;/code&gt;, which didn’t even exist.&lt;/p&gt;

&lt;p&gt;For scenarios like this, you still need strong debugging and code reading skills. I cover those skills and more in &lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=read-invalid-fields-null-csvhelper&quot;&gt;Street-Smart Coding&lt;/a&gt;&lt;/em&gt;—A roadmap with 30 lessons to help you code like a pro.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Jesus on prototyping, C# union types, and taste</title>
   <link href="https://canro91.github.io/2026/04/10/FridayLinks/"/>
   <updated>2026-04-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/10/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; After a long wait, &lt;a href=&quot;https://devblogs.microsoft.com/dotnet/csharp-15-union-types/&quot;&gt;C# union types are here&lt;/a&gt; (10min). Well, in the next C# version. If you’re new to union types, I wrote &lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;this guide&lt;/a&gt; (9min) when Microsoft announced the feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; If you’re new to a codebase, run &lt;a href=&quot;https://piechowski.io/post/git-commands-before-reading-code/&quot;&gt;these Git commands&lt;/a&gt; (5min) before reading any code. Treat it like a crime scene: gather evidence before touching anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; When AI makes code and text free, &lt;a href=&quot;https://rajnandan.com/posts/taste-in-the-age-of-ai-and-llms/&quot;&gt;the only skills left are taste and judgment&lt;/a&gt; (10min). I liked the exercise of &lt;em&gt;“fails because.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Did you know that Jesus taught about prototyping with an LLM? Here’s &lt;a href=&quot;https://blog.jim-nielsen.com/2026/prototyping-with-llm/&quot;&gt;his advice&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, not much about coding on my blog, but I wrote about &lt;a href=&quot;/2026/04/07/30Years/&quot;&gt;the secret to surviving 30 years in one job (and to live a happy life)&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Street-Smart Coding&lt;/a&gt;, 30 lessons to help you code like a pro. From Googling to clear communication, it shares the lessons to help you stand out in the age of AI.&lt;/p&gt;

&lt;p&gt;See you next Friday with more links.&lt;/p&gt;

&lt;p&gt;Keep coding smartly.&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Real Effect of a Daily Writing Habit</title>
   <link href="https://canro91.github.io/2026/04/08/WriteDaily/"/>
   <updated>2026-04-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/08/WriteDaily</id>
   <content type="html">&lt;p&gt;My first writing class shocked me.&lt;/p&gt;

&lt;p&gt;Its main lesson was to &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;write every day&lt;/a&gt;.
It was a webinar pitching a writing course.
But it made me revive my blog after abandoning it for months.&lt;/p&gt;

&lt;p&gt;Since &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;November 1st, 2024&lt;/a&gt;, I’ve been writing daily.
If I miss a day, I write two posts to recover the streak.
More than once I’ve thought &lt;a href=&quot;/2025/05/20/200DailyPosts/&quot;&gt;I’d run out of ideas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Writing daily makes you pay attention to the world.
You see the world through your writer’s glasses.
This is my favorite side effect.&lt;/p&gt;

&lt;p&gt;As Robert Greene says, &lt;a href=&quot;/2025/05/21/Material/&quot;&gt;“it’s all material.”&lt;/a&gt;
A conversation could be a mini-story.
A failed delivery, &lt;a href=&quot;/2025/07/30/BuyingAMattress/&quot;&gt;a business lesson&lt;/a&gt;.
A visit to the grocery store, &lt;a href=&quot;/2026/04/07/30Years/&quot;&gt;a life lesson&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Writing daily is &lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;a habit that has truly changed my life&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Write Not to Be Read (Yes, You Read That Right)</title>
   <link href="https://canro91.github.io/2026/04/07/NotRead/"/>
   <updated>2026-04-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/07/NotRead</id>
   <content type="html">&lt;p&gt;Almost nobody reads online.&lt;/p&gt;

&lt;p&gt;We don’t deserve attention, especially when content is free and abundant.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2026/01/07/SmartBrevity/&quot;&gt;Smart Brevity&lt;/a&gt; says we have a few milliseconds to earn attention:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Write &lt;a href=&quot;/2025/07/15/Headlines/&quot;&gt;an attention-grabbing headline&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Open with &lt;a href=&quot;/2025/06/04/OpeningLines/&quot;&gt;strong first line&lt;/a&gt;. Or jump straight to your main point without preamble.&lt;/li&gt;
  &lt;li&gt;Write short pieces or break longer ones with subheaders.&lt;/li&gt;
  &lt;li&gt;Use short paragraphs with short sentences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most people will only read your headline, subheaders, and bold sentences. Tell them what they need to know if that’s all they do.&lt;/p&gt;

&lt;p&gt;Christopher Butler writes in &lt;a href=&quot;https://www.chrbutler.com/earning-attention&quot;&gt;Earning Attention&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Here’s another 80/20 framing–80% of your audience will never do more than scan your information; 20% will go on to read it.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Write as if nobody is reading, but everybody is skimming.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Secret to Surviving 30 Years In One Job (And to Live a Happy Life)</title>
   <link href="https://canro91.github.io/2026/04/07/30Years/"/>
   <updated>2026-04-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/07/30Years</id>
   <content type="html">&lt;h2 id=&quot;in-a-line-at-the-grocery-store&quot;&gt;In a line at the grocery store…&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“Sorry, did I mishear, you said you’ve been here 35 years?”&lt;/em&gt; I asked her.&lt;/p&gt;

&lt;p&gt;While waiting, I skimmed the books on the shelves and overheard the cashier talking to the client in front. When it was finally my turn, I couldn’t help but ask her.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“30! I have 30 years,”&lt;/em&gt; the cashier told me. &lt;em&gt;“The girl at the bakery section has 35. She was about to leave last year. She’s leaving next May.”&lt;/em&gt; It was April.&lt;/p&gt;

&lt;h2 id=&quot;staying-for-so-long-at-a-place&quot;&gt;Staying for so long at a place…&lt;/h2&gt;

&lt;p&gt;Curious and surprised, I asked her, &lt;em&gt;“How did you manage to stay so long at a single place?”&lt;/em&gt; To me, 30 years at the same job sounds like a life sentence.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Take it easy!”&lt;/em&gt; she said while smiling. &lt;em&gt;“Don’t let anyone ruin your day!”&lt;/em&gt; She kept scanning items without pause. &lt;em&gt;“How long have you been in your job?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Earlier that day, a pointless task from a freelancing client made me want to throw my laptop against the wall. Her words came at the perfect time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I got bored or they got bored of me. The longest I’ve stayed at a place is 5 years.”&lt;/em&gt; That was at my last full-time job as a coder. &lt;a href=&quot;/2025/05/13/PrioritizeHealth/&quot;&gt;I burned out&lt;/a&gt; while waiting for a promotion. Months later, &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;the company laid off&lt;/a&gt; almost everyone, including me.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“You have to take it easy. There are clients that…“&lt;/em&gt; she said while shaking her head.&lt;/p&gt;

&lt;h2 id=&quot;when-someone-does-it&quot;&gt;When someone does it…&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“There’s only one who ruined my days…“&lt;/em&gt; The smile on her face disappeared.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“A boss?”&lt;/em&gt; I asked her while expecting to hear some gossip or horror stories from working at a grocery store.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“My first husband… That one sucked the life out of me. That’s why I left him.”&lt;/em&gt; Beep… Beep… She kept passing the products all that time. &lt;em&gt;“I was skinny… When I left him, everybody said they could see the change.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Years ago, 30 years in one place sounded like a career wasted. Now I know some people thrive in structure. Others in change. Others take a boring job to fuel their hobbies. Whatever your path, smile, take it easy, and never let anyone suck the life out of you. And if it’s your partner or your job doing it, leave them.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>When You Feel Lost, Find Your Heroes</title>
   <link href="https://canro91.github.io/2026/04/05/FindYourHeroes/"/>
   <updated>2026-04-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/05/FindYourHeroes</id>
   <content type="html">&lt;p&gt;In 2023, after burning out, I felt completely lost.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/05/13/PrioritizeHealth/&quot;&gt;A daily wellness routine&lt;/a&gt; to care for my body, mind, and spirit saved me.
&lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;That simple idea changed my life&lt;/a&gt;.
I still follow it today.
No excuses.&lt;/p&gt;

&lt;p&gt;Inspiring people helped &lt;a href=&quot;/2025/08/11/Present/&quot;&gt;my burnout recovery&lt;/a&gt;.
I filled my days reading &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;Dan Koe&lt;/a&gt;, and Borja Vilaseca.
I looked up to them.&lt;/p&gt;

&lt;p&gt;More recently, I’ve added &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;Seth Godin&lt;/a&gt; and &lt;a href=&quot;/2026/04/04/DerekSivers/&quot;&gt;Derek Sivers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;They had something in common:
&lt;a href=&quot;/2025/09/23/Reinvention/&quot;&gt;Reinvention&lt;/a&gt;, living on their own terms, eating what they hunt, &lt;a href=&quot;/2025/04/01/WhyIWillKeepWriting/&quot;&gt;writing&lt;/a&gt;, books…
That’s the path I’m trying to follow now.&lt;/p&gt;

&lt;p&gt;When lost, find the thread between your heroes.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Five Writing Lessons from Derek Sivers (From Circus Performer to Entrepreneur to Writer)</title>
   <link href="https://canro91.github.io/2026/04/04/DerekSivers/"/>
   <updated>2026-04-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/04/DerekSivers</id>
   <content type="html">&lt;p&gt;A weekend on YouTube led me to &lt;a href=&quot;/2026/03/30/YouTubeRecommendations/&quot;&gt;a writing channel&lt;/a&gt;, and then to rediscover Derek Sivers.&lt;/p&gt;

&lt;p&gt;If you haven’t heard of him, he’s the definition of someone who &lt;a href=&quot;/2025/09/23/Reinvention/&quot;&gt;reinvented their life&lt;/a&gt;. He went from circus performer to running &lt;em&gt;CD Baby,&lt;/em&gt; an online distribution platform. After selling it, he now writes books. &lt;em&gt;How to Live,&lt;/em&gt; is his most recent and popular.&lt;/p&gt;

&lt;p&gt;Here’s what I learned binge-watching his interviews and reading &lt;a href=&quot;https://sive.rs/&quot;&gt;his blog&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Be the entertainer.&lt;/strong&gt; Your writing and stories aren’t about you. They’re about how to help your readers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Write succinct books.&lt;/strong&gt; Call it a tiny book, &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;a mini book&lt;/a&gt;, or simply a short book, it’s your best ideas distilled. A short book shows respect for your readers’ time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Write essays then turn them into books.&lt;/strong&gt; A book could be a single idea or a collection of multiple ideas.&lt;/p&gt;

&lt;p&gt;Start your book by writing essays. Then compile the best ones into a book. You’ll have proven and validated ideas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Write to scale yourself.&lt;/strong&gt; When you want to stop talking about a subject, write a book. Next asked again, point to the book. That’s why Derek wrote &lt;em&gt;Anything You Want.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Like Derek, before retiring from coding, my goal is to write a trilogy. &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt; is the second installment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Focus on writing sentences.&lt;/strong&gt; &lt;a href=&quot;/2026/03/31/BetterSentences/&quot;&gt;Write each sentence on a separate line&lt;/a&gt;. Then, write something shorter than you could.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>4 Mind-Changing Lessons to Start an Online Business the Purposeful Way</title>
   <link href="https://canro91.github.io/2026/04/03/PurposeAndProfit/"/>
   <updated>2026-04-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/03/PurposeAndProfit</id>
   <content type="html">&lt;p&gt;Forget LLCs or investors. Starting a business is simpler.&lt;/p&gt;

&lt;p&gt;Being an entrepreneur is often mistaken for hustling and grinding long hours. In &lt;em&gt;Purpose and Profit,&lt;/em&gt; Dan Koe, &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;a millionaire creator&lt;/a&gt;, shares &lt;a href=&quot;/2026/04/03/PurposeAndProfit/&quot;&gt;a different view of entrepreneurship&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;1-entrepreneurship-doesnt-have-to-be-complicated&quot;&gt;#1. Entrepreneurship doesn’t have to be complicated&lt;/h2&gt;

&lt;p&gt;No more working long hours. No more growing a business while &lt;a href=&quot;/2025/05/01/AvoidDoctors/&quot;&gt;paying with your health&lt;/a&gt;. Entrepreneurship is building skills to help others.&lt;/p&gt;

&lt;h2 id=&quot;2-entrepreneurship-is-self-help-and-others-help&quot;&gt;#2. Entrepreneurship is self-help and others-help&lt;/h2&gt;

&lt;p&gt;Being a CEO is a title. But being an entrepreneur is a mindset.&lt;/p&gt;

&lt;p&gt;You’re an entrepreneur when you solve your own problems and share your solution to help others.&lt;/p&gt;

&lt;p&gt;Nobody will pay you unless you help them. To earn more, become more valuable: solve bigger problems or help more people. As an entrepreneur, money is the result of your personal development.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“If entrepreneurship is about solving problems and self-actualization is solving your own, you can combine both into a meaningful way of life”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;3-become-a-one-man-media-company&quot;&gt;#3. Become a one-man media company&lt;/h2&gt;

&lt;p&gt;Your product is what you created to solve your problem. Your audience is people with the same problem. Your platform is the internet.&lt;/p&gt;

&lt;p&gt;At first, &lt;a href=&quot;/2025/02/01/NewDefinitionOfMoney/&quot;&gt;nobody will care about you and what you do&lt;/a&gt;. To make them care, you need to position your solution as valuable. Learn persuasion to reach the right audience—people like you.&lt;/p&gt;

&lt;h2 id=&quot;4-youre-the-niche&quot;&gt;#4. You’re the niche&lt;/h2&gt;

&lt;p&gt;When you solve your problems, you’ll find new and bigger problems. You won’t be limited by a social media bio or a tagline. As you solve them, your niche will evolve with you. &lt;em&gt;“Your life’s work is getting paid to be yourself.”&lt;/em&gt; Entrepreneurship is evolving.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Axios hacked, QA rethink, and roulette</title>
   <link href="https://canro91.github.io/2026/04/03/FridayLinks/"/>
   <updated>2026-04-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/03/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 stories that caught my eye last week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Last week, another npm package was hacked. This time, &lt;a href=&quot;https://techcrunch.com/2026/03/31/hacker-hijacks-axios-open-source-project-used-by-millions-to-push-malware/&quot;&gt;it was Axios’ turn&lt;/a&gt; (3min). If you’ve downloaded it recently, patch now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Not that testing isn’t important, but should we have QA teams? That’s &lt;a href=&quot;https://www.rubick.com/should-qa-exist/&quot;&gt;another role AI makes us rethink&lt;/a&gt; (11min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Not a big surprise, but &lt;a href=&quot;https://browsergate.eu/&quot;&gt;LinkedIn is searching and scraping your browser&lt;/a&gt; (3min). Time for better fingerprinting protection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Last week’s cool tool: &lt;a href=&quot;https://twitchroulette.net&quot;&gt;twitchroulette&lt;/a&gt; I spun the roulette a few times and only found game streams. Maybe you’ll be luckier and find coding streams.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/03/18/SlowDown/&quot;&gt;the hidden truth AI has revealed&lt;/a&gt; (1min) &lt;a href=&quot;/2026/03/28/ExcelParadox/&quot;&gt;the Excel paradox of coding&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Street-Smart Coding&lt;/a&gt;, 30 lessons to help you code like a pro. From Googling to clear communication, it shares the lessons to help you stand out in the age of AI.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Street-Smart Coding: A Review That Made My Day</title>
   <link href="https://canro91.github.io/2026/04/01/Review/"/>
   <updated>2026-04-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/04/01/Review</id>
   <content type="html">&lt;p&gt;No coding tip today. Just celebrating a small win that remind me why I started writing.&lt;/p&gt;

&lt;p&gt;Street-Smart Coding got a review on dev.to that made my day.&lt;/p&gt;

&lt;p&gt;Here’s &lt;a href=&quot;https://dev.to/baltasarq/cesar-aguirres-street-smart-coding-31ec&quot;&gt;the original post&lt;/a&gt; in Spanish. Let me translate the gist of it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“…It is written from experience. In other words, it doesn’t tell you how things should be done, but rather how the author personally learned how things should be done.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s exactly the point! I wrote the guide I wish I had. A proven roadmap from junior to senior coder. I wanted it to feel like a conversation over coffee.&lt;/p&gt;

&lt;p&gt;With a couple of prompts or searches, you could find tutorials on syntax. &lt;a href=&quot;/tags/tutorial&quot;&gt;I’ve written my own&lt;/a&gt;. But inside &lt;em&gt;Street-Smart Coding&lt;/em&gt;, you’ll find 30 lessons to level up your coding skills:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Some conventional&lt;/li&gt;
  &lt;li&gt;Some learned the hard way&lt;/li&gt;
  &lt;li&gt;And a few… weird ones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All tested in the real world.&lt;/p&gt;

&lt;p&gt;I wrote it for my past self and for every coder who’s ever asked “how to get better at coding.”&lt;/p&gt;

&lt;p&gt;If you haven’t already, &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=review-that-made-my-day&quot;&gt;grab your copy here&lt;/a&gt; and start coding smartly.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Simple Exercise to Instantly Improve Your Sentences</title>
   <link href="https://canro91.github.io/2026/03/31/BetterSentences/"/>
   <updated>2026-03-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/31/BetterSentences</id>
   <content type="html">&lt;p&gt;Athletes train every day.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;Writing shouldn’t be any different&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Practice writing each sentence on a separate line.&lt;/p&gt;

&lt;p&gt;Derek Sivers recommends this technique.&lt;/p&gt;

&lt;p&gt;The goal is noticing sentence length.&lt;/p&gt;

&lt;p&gt;Long sentences.&lt;/p&gt;

&lt;p&gt;Short ones.&lt;/p&gt;

&lt;p&gt;Variety!&lt;/p&gt;

&lt;p&gt;Separate lines highlight start and end words.&lt;/p&gt;

&lt;p&gt;Like a joke, they’re what people remember.&lt;/p&gt;

&lt;p&gt;They’re the strongest words.&lt;/p&gt;

&lt;p&gt;Start with a boom.&lt;/p&gt;

&lt;p&gt;End with a punch.&lt;/p&gt;

&lt;p&gt;Put the lines together, your sentences instantly improve.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two YouTube Channels I&apos;ve Binge-Watched This Weekend</title>
   <link href="https://canro91.github.io/2026/03/30/YouTubeRecommendations/"/>
   <updated>2026-03-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/30/YouTubeRecommendations</id>
   <content type="html">&lt;h2 id=&quot;1-fabio-cerpellonis-tiny-books&quot;&gt;#1. Fabio Cerpelloni’s Tiny Books&lt;/h2&gt;

&lt;p&gt;A podcast interview with Seth Godin, the godfather of &lt;a href=&quot;[The Main Benefit of a Daily Writing Habit: 100 Daily Posts Reflection](/2025/02/09/100DailyPosts/)&quot;&gt;daily blogging&lt;/a&gt;, led me down a YouTube rabbit hole.&lt;/p&gt;

&lt;p&gt;During the interview, they mentioned &lt;em&gt;The Practice&lt;/em&gt;, one of Seth’s books. Curious, I Googled it and found &lt;a href=&quot;https://www.youtube.com/@fabiocerpelloni/videos&quot;&gt;Fabio Cerpelloni’s channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;He interviews writers, reviews books, and inspires others to write &lt;em&gt;tiny books&lt;/em&gt;. His &lt;a href=&quot;https://www.youtube.com/watch?v=mFR9QlM4Qg4&quot;&gt;questions to discover the book inside you&lt;/a&gt; gave me ideas for at least two more books.&lt;/p&gt;

&lt;p&gt;His videos feel like an unscripted conversation, like asking a friend about &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;writing books&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-the-kings-hand&quot;&gt;#2. The Kings Hand&lt;/h2&gt;

&lt;p&gt;Doomscrolling is bad, right? It’s a black hole for our time and attention.&lt;/p&gt;

&lt;p&gt;The other day, &lt;a href=&quot;/2025/07/29/InstagramLesson/&quot;&gt;scrolling taught me a valuable life lesson&lt;/a&gt;. And this time, I discovered a hidden gem about creativity, &lt;a href=&quot;https://www.youtube.com/@TheKingsHand1/videos&quot;&gt;The Kings Hand&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This channel is about creativity in visual arts and music. Its official description is &lt;em&gt;cultural strategy and creative psychology breakdowns for ambitious creators.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Binge-watching The Kings Hand made me ask about my visual identity. A silhouette of a tuxedoed man leaning forward with his hand in his hat instantly recalls Michael Jackson. That’s the power of visual identity.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>You Have A Story to Tell Hidden in Plain Sight</title>
   <link href="https://canro91.github.io/2026/03/30/Bookshelf/"/>
   <updated>2026-03-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/30/Bookshelf</id>
   <content type="html">&lt;p&gt;Every book worm already has a story worth telling.&lt;/p&gt;

&lt;p&gt;The books you read reflect who you are and the path you’ve taken. Your bookshelf hides plenty of stories, among them lies your own autobiography.&lt;/p&gt;

&lt;p&gt;After a year or two of ebooks, I’m back to physical books. &lt;a href=&quot;/2026/03/09/ReadingRules/&quot;&gt;My new rule is&lt;/a&gt;: &lt;em&gt;build a pile, go throught it, and build another.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;my-old-pile-already-tells-my-recent-story&quot;&gt;My old pile already tells my recent story&lt;/h2&gt;

&lt;p&gt;When I was making a good salary at my last full time job, I picked &lt;em&gt;Rich Dad Poor Dad&lt;/em&gt;, &lt;em&gt;The Richest Man in Babylon&lt;/em&gt;, and &lt;em&gt;Psychology of Money&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Then, I switched to &lt;em&gt;How to Win Friends and Influence People&lt;/em&gt;, &lt;em&gt;The Monk Who Sold His Ferrari&lt;/em&gt;, and other personal development books. Most of them came from &lt;a href=&quot;/2024/11/26/FindingMentors/&quot;&gt;a not-mentor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When &lt;a href=&quot;/2025/05/13/PrioritizeHealth/&quot;&gt;burnout knocked at my door&lt;/a&gt;, I picked &lt;em&gt;Unwinding Anxiety&lt;/em&gt; and &lt;em&gt;The Ruthless Elimination of Hurry&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When I hit rock-bottom, I discovered &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt; and his books, &lt;em&gt;Choose Yourself&lt;/em&gt;, &lt;em&gt;&lt;a href=&quot;/2025/01/19/ChooseYourselfGuideToWealth/&quot;&gt;Choose Yourself Guide to Wealth&lt;/a&gt;&lt;/em&gt;, and &lt;em&gt;&lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Skip the Line&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After recovering from burnout, writing became my therapy. To improve my writing, I started with &lt;em&gt;The Post Office&lt;/em&gt; and &lt;em&gt;Jesus’ Son&lt;/em&gt;. And sitting on my bookshelf, I have &lt;em&gt;Angel and Demons&lt;/em&gt; and &lt;em&gt;The Old Man and The Sea&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Just by looking at my bookshelf, I have a story to tell and a book to write. What story is your bookshelf hiding? If you think you don’t have anything to share, look twice at the books you’ve read.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Excel Paradox of Coding</title>
   <link href="https://canro91.github.io/2026/03/28/ExcelParadox/"/>
   <updated>2026-03-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/28/ExcelParadox</id>
   <content type="html">&lt;p&gt;Last week, for the nth time, I had to bulk-import records using Excel.&lt;/p&gt;

&lt;p&gt;No matter how advanced and complex your business rules and code, they often circle back to reading and writing Excel files.&lt;/p&gt;

&lt;p&gt;I should call it &lt;em&gt;The Excel paradox of coding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead of writing enterprise software, maybe we should build Excel add-ons and let end users stick to what they know, Excel.&lt;/p&gt;

&lt;h2 id=&quot;tasks-for-bulk-importing-from-excel-files&quot;&gt;Tasks for bulk-importing from Excel files&lt;/h2&gt;

&lt;p&gt;If you’re starting out, here’s a street‑smart tip:&lt;/p&gt;

&lt;p&gt;Learn to work with Excel in your language of choice to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Download an Excel file with existing records.&lt;/li&gt;
  &lt;li&gt;Upload an Excel file with updated columns, using an ID column to find matching records.&lt;/li&gt;
  &lt;li&gt;Validate data integrity of the file. Be careful with date and numeric columns.&lt;/li&gt;
  &lt;li&gt;Bulk-update the records from the file. Optionally, use &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;a background processor&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Report once the file is processed. Optionally, report the progress with a completion bar on a page and send an email when done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’ll save you countless headaches. You’ll use it a lot! I’ve seen those tasks in every single job I’ve had.&lt;/p&gt;

&lt;p&gt;That lesson didn’t make it into &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=excel-paradox-of-coding&quot;&gt;Street‑Smart Coding&lt;/a&gt;, but inside you’ll find 30 practical lessons to level up your coding skills.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>12 Interesting Questions to Ask</title>
   <link href="https://canro91.github.io/2026/03/27/Questions/"/>
   <updated>2026-03-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/27/Questions</id>
   <content type="html">&lt;p&gt;Here’s my &lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;10-idea list&lt;/a&gt; today—Well, I wrote 12:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;What can I only do?&lt;/li&gt;
  &lt;li&gt;Who can I ask for help?&lt;/li&gt;
  &lt;li&gt;How can I help more people?&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/15/FightingAIContent/&quot;&gt;What interesting stories can I tell?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;What “unscalable” things can I do?&lt;/li&gt;
  &lt;li&gt;What new context can I put my work in?&lt;/li&gt;
  &lt;li&gt;What existing ideas can I combine in new ways?&lt;/li&gt;
  &lt;li&gt;How can I get out of my comfort zone?&lt;/li&gt;
  &lt;li&gt;How can I become more valuable?&lt;/li&gt;
  &lt;li&gt;What visual identity can I use?&lt;/li&gt;
  &lt;li&gt;What experiments can I run?&lt;/li&gt;
  &lt;li&gt;What can I simplify?&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: AI, Excel, and nostalgic sounds</title>
   <link href="https://canro91.github.io/2026/03/27/FridayLinks/"/>
   <updated>2026-03-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/27/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;This week, for the nth time, I had to bulk import records via an Excel file. It made me think, no matter how complex the code, it often boils down to reading and writing Excel files. Maybe we should build Excel add-ons or plugins instead of enterprise software. Thoughts?&lt;/p&gt;

&lt;p&gt;Anyway, here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; &lt;a href=&quot;https://kellblog.com/2026/03/19/why-im-not-worried-about-running-out-of-work-in-the-age-of-ai/&quot;&gt;Worried about running out of work in the age of AI?&lt;/a&gt; (13min). We can learn from the auto industry. And no, they didn’t run out of work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; I’m always amazed by how much you can accomplish with the command line. Here are &lt;a href=&quot;https://will-keleher.com/posts/small-programming-tricks-matter/&quot;&gt;some small programming tricks&lt;/a&gt; (6min), some of them for the CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Headlines saying &lt;a href=&quot;https://stevekrouse.com/precision&quot;&gt;coding is dead are exaggerated&lt;/a&gt; (10min). Coding isn’t going anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; For a bit of nostalgia, here’s &lt;a href=&quot;https://citiesandmemory.com/obsolete-sounds/&quot;&gt;a list of obsolete sounds&lt;/a&gt;. The modem dial-up…Anyone else?&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/03/24/CareerIntention/&quot;&gt;why you need an intention for a successful career&lt;/a&gt; (3min). That was a follow-up on &lt;a href=&quot;/2026/03/18/WorstJob/&quot;&gt;the most painful mistake from my best job&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-ai-excel-nostalgic-sounds&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. If you’ve wondered how to actually change your life, this book shares 10 small daily ideas for big change.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This Simple Exercise Crushes Writer&apos;s Block In Four Minutes</title>
   <link href="https://canro91.github.io/2026/03/25/FourMinutes/"/>
   <updated>2026-03-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/25/FourMinutes</id>
   <content type="html">&lt;p&gt;There’s nothing more intimidating than a blank page.&lt;/p&gt;

&lt;p&gt;That was me today. I scrolled Hacker News, minifeed, and LinkedIn. I was looking for &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;a subject to steal&lt;/a&gt;, but nothing inspired me.&lt;/p&gt;

&lt;p&gt;Yesterday was easier. &lt;a href=&quot;/2026/03/24/CareerIntention/&quot;&gt;The most painful lesson from my best job&lt;/a&gt; sparked a discussion on &lt;a href=&quot;https://dev.to/canro91/the-most-painful-career-lesson-my-best-job-taught-me-ca2&quot;&gt;dev.to&lt;/a&gt;. I just &lt;a href=&quot;/2026/03/24/CareerIntention/&quot;&gt;replied to that discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Waiting for the muses, I tried a course called &lt;em&gt;Write 4 a day.&lt;/em&gt; The first exercise was to write by hand for four minutes, whatever comes to mind, without judgment.&lt;/p&gt;

&lt;p&gt;I had nothing to lose. So I tried it.&lt;/p&gt;

&lt;p&gt;This exercise is like a 4-minute version of Julia Cameron’s Morning Pages, at anytime. It works: I’m writing these lines.&lt;/p&gt;

&lt;p&gt;If you’re blocked, grab pen and write for 4 minutes. That’s all you need to unlock your words.&lt;/p&gt;

&lt;p&gt;If after 4 minutes, you’re still blocked: &lt;a href=&quot;/2024/12/07/WritersBlock/&quot;&gt;try this trick&lt;/a&gt; or &lt;a href=&quot;/2025/02/22/WritingPrompts/&quot;&gt;any of these prompts&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why You Need An Intention For Your Coding Career</title>
   <link href="https://canro91.github.io/2026/03/24/CareerIntention/"/>
   <updated>2026-03-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/24/CareerIntention</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Last week, I wrote about &lt;a href=&quot;/2026/03/18/WorstJob/&quot;&gt;the most painful lesson my best job taught me&lt;/a&gt;. Recently, I shared it on &lt;a href=&quot;https://dev.to/canro91/the-most-painful-career-lesson-my-best-job-taught-me-ca2&quot;&gt;dev.to&lt;/a&gt;. The concept of a “plan” generated some discussion. Here I’m expanding on that.&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;It took me over 10 years to connect the dots.&lt;/p&gt;

&lt;p&gt;For years, I didn’t have a career plan. I jumped from job to job feeling something was missing. OK, when I say “jump,” I mean fired, bored, and laid off. That was &lt;a href=&quot;/2026/03/18/WorstJob/&quot;&gt;my most painful lesson&lt;/a&gt;. It cost me my health at the lowest point.&lt;/p&gt;

&lt;p&gt;A “plan” sounds like a blueprint with every career scenario figured out in advance.&lt;/p&gt;

&lt;p&gt;Nobody starts with a perfect plan. The early stages of our careers are about discovery, experimentation, and &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;building skills&lt;/a&gt; while learning to navigate the corporate world.&lt;/p&gt;

&lt;p&gt;Plans are hard to follow when &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;layoffs are always around the corner&lt;/a&gt; and &lt;a href=&quot;/2025/11/23/TranslatorsVsAI/&quot;&gt;AI is changing job descriptions&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;an-intention-helps-you-decide-when-to-move&quot;&gt;An intention helps you decide when to move&lt;/h2&gt;

&lt;p&gt;Instead of “plan,” think of an intention: a guiding principle that helps you decide whether to stay or move on.&lt;/p&gt;

&lt;p&gt;There’s always luck, setbacks, and resets. It’s impossible to account for all of them in a “plan.” But an intention pushes you to act instead of waiting.&lt;/p&gt;

&lt;p&gt;Here’s how an intention helps you in practice.&lt;/p&gt;

&lt;p&gt;If your intention is money, a couple of years without raises or bonuses should push you to move. That’s not the job that will fill your pockets. Maybe a better choice would be joining a startup in early stages.&lt;/p&gt;

&lt;p&gt;If your intention is growing a network, sitting in a cubicle isn’t the best idea. A better role might be DevRel, evangelist, or consultant, positions that takes you out of your cubicle.&lt;/p&gt;

&lt;p&gt;Money was just an example. It could be gaining leadership experience, climbing the ladder, or learning opportunities.&lt;/p&gt;

&lt;p&gt;Use your career intention as your flashlight. Otherwise, wait to leave only when bored, fired, or &lt;a href=&quot;/2025/03/22/IsBurnoutInevitable/&quot;&gt;burned out&lt;/a&gt;. I wish someone had told me that &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;when I started my coding career&lt;/a&gt;. It would have saved lots of headaches. Set your intention today.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you liked these lessons, you’re going to like, &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=why-you-need-intention-your-coding-career&quot;&gt;Career Lessons From the Trenches&lt;/a&gt;, my free 7-day email course where I distill 10+ years of career lessons into 7 short emails–to help you navigate your coding career.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The #1 Benefit of Consistently Writing Online</title>
   <link href="https://canro91.github.io/2026/03/23/TimeCapsule/"/>
   <updated>2026-03-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/23/TimeCapsule</id>
   <content type="html">&lt;p&gt;At first, I only wrote when I had something to share. That was once or twice a year.&lt;/p&gt;

&lt;p&gt;Then, I challenged myself to write every other week. I was playing the SEO game. I packed headlines with keywords and wrote posts to land on Google’s first page.&lt;/p&gt;

&lt;p&gt;Years later, &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;to recover from burnout&lt;/a&gt;, I pushed myself to &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;write daily&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A daily writing practice taught me to find ideas quickly, write away from my desk, and build a creative routine.&lt;/p&gt;

&lt;h2 id=&quot;the-time-capsule-effect&quot;&gt;The Time Capsule Effect&lt;/h2&gt;

&lt;p&gt;Apart from those three benefits, the #1 benefit is what I call the &lt;em&gt;time capsule effect.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After &lt;a href=&quot;/2026/03/16/500DailyPosts/&quot;&gt;+600 daily posts&lt;/a&gt;, I can jump to almost any date to see what I was learning, struggling, or sharing. A couple of years ago, I was learning &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;how to outline a book&lt;/a&gt; and wondering &lt;a href=&quot;/2025/01/23/CallingYourselfAWriter/&quot;&gt;when to call myself a writer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Reading old posts and thinking &lt;em&gt;“I’d do it better today”&lt;/em&gt; shows &lt;a href=&quot;/2025/12/23/SignOfGrowth/&quot;&gt;how much our skills have improved&lt;/a&gt;. That only happens with consistent practice.&lt;/p&gt;

&lt;p&gt;Show up consistently, and you’ll build a time capsule that future-you will treasure.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>4 Fresh Ideas On Monetization and Marketing from Last Week</title>
   <link href="https://canro91.github.io/2026/03/22/RandomIdeas/"/>
   <updated>2026-03-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/22/RandomIdeas</id>
   <content type="html">&lt;p&gt;Yesterday, I shared &lt;a href=&quot;/2026/03/21/RandomIdeas/&quot;&gt;5 Ideas on growth and money&lt;/a&gt;. To build on that, here are 4 fresh ideas about content creation, monetization, and networking from last week:&lt;/p&gt;

&lt;h2 id=&quot;1-run-a-1month-club&quot;&gt;#1. Run a $1/month club&lt;/h2&gt;

&lt;p&gt;Monetizing your content doesn’t have to be complicated.&lt;/p&gt;

&lt;p&gt;If sales make you nervous, offer a simple $1/month membership. No fancy tiers. No paywalls. Just $1. Manuel Moreale made &lt;a href=&quot;https://manuelmoreale.com/thoughts/one-a-month&quot;&gt;the concept popular&lt;/a&gt;. Now there’s a &lt;a href=&quot;https://oneamonth.club/&quot;&gt;club of $1/month creators&lt;/a&gt;. That doesn’t sound that bad at all. Maybe I’ll join the club.&lt;/p&gt;

&lt;h2 id=&quot;2-follow-roots&quot;&gt;#2. Follow ROOTS&lt;/h2&gt;

&lt;p&gt;I follow &lt;a href=&quot;/2024/12/15/BloggingWorkflow/&quot;&gt;POSSE&lt;/a&gt;, aka using your website as your content hub.&lt;/p&gt;

&lt;p&gt;But last week I learned about &lt;a href=&quot;https://social.emucafe.org/naferrell/return-old-online-things-to-your-own-site-03-20-26/&quot;&gt;ROOTS&lt;/a&gt;, &lt;em&gt;Return Old Online Things to your own Site.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you’ve guest posted or collaborated, archive those posts on your site. I have at least 4 or 5 posts that I could “root.”&lt;/p&gt;

&lt;h2 id=&quot;3-a-90-day-note&quot;&gt;#3. A 90-day note&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;Losing my job&lt;/a&gt; has taught me the value of networking. Most job applications hide behind phone calls and recommendations.&lt;/p&gt;

&lt;p&gt;If you wait until you lose your job to network, it’s too late. Why not keep in touch with colleagues and friends with a quarterly email? It’s like catching up at a party, but via email.&lt;/p&gt;

&lt;p&gt;Jason Shen, a business coach, started &lt;a href=&quot;https://www.fastcompany.com/40414066/the-networking-secret-that-only-requires-writing-four-emails-a-year&quot;&gt;sending that email years ago&lt;/a&gt;. He calls them a 90-day note.&lt;/p&gt;

&lt;h2 id=&quot;4-write-52-ps-to-promote-your-books&quot;&gt;#4. Write 52 PS to promote your books&lt;/h2&gt;

&lt;p&gt;You can’t write a book and expect endless sales. That rarely happens.&lt;/p&gt;

&lt;p&gt;Like any other product, your book requires promotion. But you don’t need podcast tours, TV appearances, or expensive agencies.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;/2025/10/12/1DollarTest/&quot;&gt;write more books to promote your earlier ones&lt;/a&gt;. Or, as Chris Stanley suggests, you can include 52 PS in your content, promoting your book. This reminds me of Mark Thompson’s &lt;a href=&quot;/2025/10/11/ContentThatSells/&quot;&gt;Next Step&lt;/a&gt; strategy.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Fresh Ideas On Growth And Money from Last Week</title>
   <link href="https://canro91.github.io/2026/03/21/RandomIdeas/"/>
   <updated>2026-03-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/21/RandomIdeas</id>
   <content type="html">&lt;p&gt;To unclutter my brain and take public notes, here are 5 interesting ideas I found last week:&lt;/p&gt;

&lt;h2 id=&quot;1-workout-using-antagonistic-supersets&quot;&gt;#1. Workout using antagonistic supersets&lt;/h2&gt;

&lt;p&gt;Improving my health led me down a fitness rabbit hole. I found Dr. Michael Israetel and &lt;a href=&quot;https://www.youtube.com/@RenaissancePeriodization&quot;&gt;his YouTube channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well, you don’t need to spend hours every day at the gym.&lt;/p&gt;

&lt;p&gt;Most gym time is wasted checking your phone or waiting for machines. Two to three full-body workout sessions per week are enough to be healthy. In just 20 minutes, you can work out every major muscle group by alternating two unrelated muscle groups with minimal rest between sets.&lt;/p&gt;

&lt;h2 id=&quot;2-scrolling-is-a-waste-of-your-potential&quot;&gt;#2. Scrolling is a waste of your potential&lt;/h2&gt;

&lt;p&gt;Last week, I shared a LinkedIn post with the idea of &lt;a href=&quot;/2026/03/13/Resistance/&quot;&gt;staying away from social media and your phone as rebellion&lt;/a&gt;. I got an insightful comment back: &lt;em&gt;“Scrolling is a waste of your human potential.”&lt;/em&gt; Wow!&lt;/p&gt;

&lt;h2 id=&quot;3-work-less-but-daily&quot;&gt;#3. Work less, but daily&lt;/h2&gt;

&lt;p&gt;If you’re a creator, freelancer, or “side-gigger,” instead of constantly hustling, aim to work for 2-3 hours, but daily. Short sessions force you to focus and achieve more.&lt;/p&gt;

&lt;h2 id=&quot;4-entrepreneurship-doesnt-have-to-be-complicated&quot;&gt;#4. Entrepreneurship doesn’t have to be complicated&lt;/h2&gt;

&lt;p&gt;You don’t need to worry about creating a business entity, the hustle culture, or complicated systems. &lt;a href=&quot;/2026/03/20/Entrepreneurship/&quot;&gt;Entrepreneurship is way simpler&lt;/a&gt;. It’s helping!&lt;/p&gt;

&lt;h2 id=&quot;5-be-more-valuable-to-make-more-money&quot;&gt;#5. Be more valuable to make more money&lt;/h2&gt;

&lt;p&gt;In &lt;em&gt;Purpose and Profit&lt;/em&gt;, Dan Koe shares that, to make money, you have to be helpful to others since nobody will give you money in exchange for nothing. To earn more, make others value you more, solve bigger problems, or help more people.&lt;/p&gt;

&lt;p&gt;That concept reminded me of &lt;a href=&quot;/2025/02/01/NewDefinitionOfMoney/&quot;&gt;money as fucks given&lt;/a&gt; and one of James Altucher’s &lt;a href=&quot;/2025/01/06/BestQuote2024/&quot;&gt;inspiring quotes&lt;/a&gt;, “money is a byproduct of personal development.”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Dead internet, 69 AI agents, and finishing books</title>
   <link href="https://canro91.github.io/2026/03/20/FridayLinks/"/>
   <updated>2026-03-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/20/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; The internet is dead. &lt;a href=&quot;https://www.adriankrebs.ch/blog/dead-internet/&quot;&gt;It doesn’t seem to be a theory anymore&lt;/a&gt; (2min). Reddit, LinkedIn, GitHub…AI-generated slop is everywhere. Even bots interviewing bots.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Every week, there’s a new AI tool, model, or prediction. But you don’t have to feel like you’re missing the whole AI train. &lt;a href=&quot;https://geohot.github.io//blog/jekyll/update/2026/03/11/running-69-agents.html&quot;&gt;If you’re not running 69 agents, you’re not behind&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; AI shines at speed. We can crank out code faster than ever. But more code means more reviews. &lt;a href=&quot;https://apenwarr.ca/log/20260316&quot;&gt;Every layer of review makes coding 10x slower&lt;/a&gt; (10min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; When was the last time you finished a book? I’m replacing social media with books. But with plenty of distractions, it seems &lt;a href=&quot;https://smallpotatoes.paulbloom.net/p/nobody-finishes-reading-my-books-eca&quot;&gt;nobody finishes books anymore&lt;/a&gt; (14min). That’s a challenge for authors and educators.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/03/13/Resistance/&quot;&gt;how we’re helping AI replace us&lt;/a&gt; (1min) and &lt;a href=&quot;/2026/03/18/WorstJob/&quot;&gt;the most painful career lesson my best job taught me&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=dead-internet-ai-agents-finishing-books&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. If you’ve wondered how to actually change your life, this book shares 10 small daily ideas for big change.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Entrepreneurship Doesn&apos;t Have To Be Complicated As You Might Think</title>
   <link href="https://canro91.github.io/2026/03/20/Entrepreneurship/"/>
   <updated>2026-03-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/20/Entrepreneurship</id>
   <content type="html">&lt;p&gt;Years ago, I thought entrepreneurship was nothing but risk.&lt;/p&gt;

&lt;p&gt;I once thought running a business was crazy: registering a company, hiring employees, waiting for clients to pay… Being an employee was better. A monthly paycheck was safer. I couldn’t have been more wrong! I was young and naive. I had to &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;be fired only once&lt;/a&gt; to change my mind.&lt;/p&gt;

&lt;p&gt;Recently, to debunk all myths about entrepreneurship, I’ve stumbled upon two simple definitions—I’m paraphrasing them to practice &lt;a href=&quot;/2026/01/25/7Words/&quot;&gt;the 7-word summary&lt;/a&gt;:&lt;/p&gt;

&lt;h2 id=&quot;1-solving-problems&quot;&gt;#1. Solving problems&lt;/h2&gt;

&lt;p&gt;From &lt;em&gt;Purpose and Profit&lt;/em&gt; by &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;Dan Koe&lt;/a&gt;, entrepreneurship is &lt;em&gt;solving your own problems and distributing your solutions to others.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You don’t need to stare at the ceiling waiting for &lt;a href=&quot;/2026/01/25/7Words/&quot;&gt;the perfect idea&lt;/a&gt;. If you have solved one of your own problems (losing weight or overcoming burnout, for example), you can share what you did and make some profit.&lt;/p&gt;

&lt;h2 id=&quot;2-making-extra-money&quot;&gt;#2. Making extra money&lt;/h2&gt;

&lt;p&gt;Recently, &lt;a href=&quot;/2026/02/16/NoFeeds1/&quot;&gt;during my feed-free week&lt;/a&gt;, I picked up &lt;em&gt;Financial Freedom&lt;/em&gt; by Grant Sabatier. Apart from early-retirement tactics, his definition of entrepreneurship resonated. He says &lt;em&gt;entrepreneurship is simply finding other income sources.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You don’t need the LLC, employees, or fancy systems from a business coach, but to make money on the side. As simple as that. Of course, he advocates investing that extra money so you can retire before your 60s.&lt;/p&gt;

&lt;p&gt;With those two definitions, entrepreneurship feels less intimidating. Maybe you’re already an entrepreneur without realizing it.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Most Painful Career Lesson My Best Job Taught Me</title>
   <link href="https://canro91.github.io/2026/03/18/WorstJob/"/>
   <updated>2026-03-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/18/WorstJob</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href=&quot;https://dev.to/challenges/wecoded-2026&quot;&gt;2026 WeCoded Challenge&lt;/a&gt;: Echoes of Experience&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The most toxic job I ever had didn’t break me.&lt;/p&gt;

&lt;p&gt;I’ve had only one job that fit all the definitions of a toxic place. Inexperienced management, competing deadlines, scope creep… To cope with the 9-5, I hung out with coworkers, worked out, and engaged in hobbies. That kept me sane. But it wasn’t the job that burned me out. It taught me enough lessons for a book.&lt;/p&gt;

&lt;p&gt;The way to the exit door was clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; &lt;em&gt;When the pain is real. So is the urge to leave.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-job-that-burned-me-out&quot;&gt;The job that burned me out&lt;/h2&gt;

&lt;p&gt;A few years later, I landed my best job.&lt;/p&gt;

&lt;p&gt;I was &lt;a href=&quot;/2025/03/03/Boundaries/&quot;&gt;working from home&lt;/a&gt;, learning new subjects, and making a good salary. It wasn’t Silicon Valley, but it was where most coders wanted to be. It turned out more painful than my “worst” job.&lt;/p&gt;

&lt;p&gt;Everything was good until the honeymoon ended. Another project doing the same tasks. No new roles for me. All seats were already taken. Same grind, same story.&lt;/p&gt;

&lt;p&gt;This time, the way out wasn’t that clear. &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;Updating a CV&lt;/a&gt; to play the hiring game made staying seem tolerable. &lt;em&gt;“The pay is good.”&lt;/em&gt; &lt;em&gt;“I don’t work overtime.”&lt;/em&gt; &lt;em&gt;“I’ll wait until I finish this project.”&lt;/em&gt; Meanwhile, hiring trends were tougher and tougher each year.&lt;/p&gt;

&lt;p&gt;The next thing I knew, I was rushing to the bathroom. It wasn’t to throw up, but I’ll spare the details.&lt;/p&gt;

&lt;p&gt;My job became a burden. I rushed to finish my daily tasks and skipped my meals. Painful mistake! That brought &lt;a href=&quot;/2026/02/22/Digestion/&quot;&gt;stomach issues&lt;/a&gt;. (Eating when stressed out isn’t a good idea.) When I least expected it, I was sick and burned out. The way down was slow. But &lt;a href=&quot;/2025/05/13/PrioritizeHealth/&quot;&gt;the way up was more painful and slower&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; &lt;em&gt;If it makes you sick, you don’t need more signs to leave.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;my-most-painful-and-expensive-career-mistake&quot;&gt;My most painful and expensive career mistake&lt;/h2&gt;

&lt;p&gt;Not having a career plan was my biggest, most painful, and expensive mistake.&lt;/p&gt;

&lt;p&gt;I didn’t stop to think what I wanted out of my career. Money, title, connections, challenge? Maybe my only plan was to gain experience and make some money. Whatever that meant for my past self.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; &lt;em&gt;Choose wisely. Or wait to leave when sick, bored, fired, or burned out.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A plan or, better yet, &lt;a href=&quot;/2026/03/24/CareerIntention/&quot;&gt;a career intention&lt;/a&gt; would have made me move out and saved me a lot of pain. But like a frog in a pot, the water wasn’t boiling, it was slowly heating up. By the time I noticed the exit sign, the damage was already done.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you liked these lessons, you’re going to like, &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=painful-career-lesson-my-best-job-taught-me&quot;&gt;Career Lessons From the Trenches&lt;/a&gt;, my free 7-day email course where I distill 10+ years of career lessons into 7 short emails–to help you navigate your coding career.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Unexpected Truth AI Reveals about Coding</title>
   <link href="https://canro91.github.io/2026/03/18/SlowDown/"/>
   <updated>2026-03-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/18/SlowDown</id>
   <content type="html">&lt;p&gt;Most people glorify AI for its speed.&lt;/p&gt;

&lt;p&gt;AI spits out code faster than any of us, with just a few magic words. Beep, beep, boop. But coding has never been the bottleneck.&lt;/p&gt;

&lt;p&gt;In corporate settings, where is most of the time spent? 99.99% of the time, it isn’t coding.&lt;/p&gt;

&lt;p&gt;It’s&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Waiting for your product owner while they talk to a customer&lt;/li&gt;
  &lt;li&gt;Waiting for DevOps to create a pipeline for your new project&lt;/li&gt;
  &lt;li&gt;Waiting for the Finance department to approve a $5 USD/month purchase&lt;/li&gt;
  &lt;li&gt;Waiting for a committee to decide what to do&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/09/04/FasterCodeReviews/&quot;&gt;Waiting for a code review&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI speeds up &lt;a href=&quot;/2025/11/10/AIUseCases/&quot;&gt;code generation&lt;/a&gt;. Everything else is as slow as it has always been. And more code means more reviews, builds, approvals, and bugs. More paperwork.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Almost Quit At 500 Daily Posts: My Reflection On Daily Blogging</title>
   <link href="https://canro91.github.io/2026/03/16/500DailyPosts/"/>
   <updated>2026-03-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/16/500DailyPosts</id>
   <content type="html">&lt;p&gt;The last 100 posts felt like a real stretch.&lt;/p&gt;

&lt;p&gt;I wanted to quit blogging for a while. But I reminded myself: like an athlete, a writer must practice daily, or at least consistently. And stumbling upon &lt;a href=&quot;https://seths.blog/2025/04/this-is-number-10000/&quot;&gt;Seth Godin’s 10,000 daily posts&lt;/a&gt; inspired me to keep pushing. I know I’m far from hitting that number.&lt;/p&gt;

&lt;p&gt;Lately, focusing on my health has hurt my streak.&lt;/p&gt;

&lt;p&gt;To &lt;a href=&quot;/2026/03/11/Sleep/&quot;&gt;have healthy sleeping patterns&lt;/a&gt;, I’ve started going to bed every day at the same time. That has meant skipping my daily post sometimes. So I tweaked &lt;a href=&quot;/2026/03/03/BloggingRules/&quot;&gt;my blogging rules&lt;/a&gt; to repair my streak the next day with another post. By the way, I also &lt;a href=&quot;/2026/03/03/Tweaks/&quot;&gt;made some changes to my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For these last 100 posts, balancing health and writing wasn’t easy, but it taught me the value of &lt;a href=&quot;/2026/01/17/DoNothing/&quot;&gt;doing nothing&lt;/a&gt; and resting after a sprint of intense work.&lt;/p&gt;

&lt;p&gt;I learned that lesson from launching another book, &lt;em&gt;&lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;&lt;/em&gt;, while trying to balance other projects. Writing that book was &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;a book experiment&lt;/a&gt; I ran in February. It already made &lt;a href=&quot;/2026/03/07/BookExperiment/&quot;&gt;its first sale&lt;/a&gt; and earned &lt;a href=&quot;/2026/03/15/BookExperiment/&quot;&gt;its first review&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;my-most-read-and-favorite-posts&quot;&gt;My most-read and favorite posts&lt;/h2&gt;

&lt;p&gt;Here are some of the most-read posts from the last 100 days:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/12/08/RepairingBlender/&quot;&gt;Two Inspiring Business Lessons From Repairing a Blender (And Why I Almost Quit Online Hustles)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/02/24/NoSocialMediaWeek/&quot;&gt;I Spent a Week Without Social Media—And Realized I’m Just Another Junkie&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/01/27/ExpertAtFailing/&quot;&gt;How to Be an Expert at Failing (and Survive to Tell the Story)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/01/20/WritingInBooks/&quot;&gt;Why You Should Write and Underline in Books&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;A Simple Idea to Reduce My Phone Time&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/03/05/AIProofed/&quot;&gt;What to Do to Thrive in the AI Era&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are my &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;100-post&lt;/a&gt;, &lt;a href=&quot;/2025/05/20/200DailyPosts/&quot;&gt;200-post&lt;/a&gt;, &lt;a href=&quot;/2025/09/01/300DailyPosts/&quot;&gt;300-post&lt;/a&gt;, and &lt;a href=&quot;/2025/12/06/400DailyPosts/&quot;&gt;400-post&lt;/a&gt; reflections.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My February Book Experiment: Getting The First Review</title>
   <link href="https://canro91.github.io/2026/03/15/BookExperiment/"/>
   <updated>2026-03-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/15/BookExperiment</id>
   <content type="html">&lt;p&gt;Last February, I started &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;a book experiment&lt;/a&gt;. It’s starting to pay off.&lt;/p&gt;

&lt;p&gt;Two weeks ago, I launched &lt;em&gt;&lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life&lt;/a&gt;&lt;/em&gt;. It got its first sale, &lt;a href=&quot;/2026/03/07/BookExperiment/&quot;&gt;making it a success&lt;/a&gt;. But I’ve kept the experiment going.&lt;/p&gt;

&lt;p&gt;For the last phase, I offered free copies as gifts to “advance readers” in exchange for an honest review.&lt;/p&gt;

&lt;p&gt;As a fan of &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;10-idea lists&lt;/a&gt;, I wrote a list of 10 e-friends, ended up messaging 12, and heard back from 6. Two read it but weren’t eligible to review. (Amazon requires $50 in purchases within the last year.) Two managed to leave reviews. One was rejected, the other approved. A 5-star review. Hooray!&lt;/p&gt;

&lt;p&gt;Here’s the review on Amazon:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2026-03-15-BookExperiment/FirstReview.png&quot; alt=&quot;First review&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Short but sweet&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;One sale plus one review. Proof that the experiment is working.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Short but sweet”&lt;/em&gt; shows you don’t need thousands of pages to write a book that matters.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Secret to Generating Good Ideas</title>
   <link href="https://canro91.github.io/2026/03/15/BadIdeas/"/>
   <updated>2026-03-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/15/BadIdeas</id>
   <content type="html">&lt;p&gt;Good ideas start as bad ones. You just need enough to find the least bad.&lt;/p&gt;

&lt;h2 id=&quot;1&quot;&gt;#1.&lt;/h2&gt;

&lt;p&gt;Tim Ferriss had different titles for his book, &lt;em&gt;4-Hour Work Week&lt;/em&gt;. Probably, there were good and bad titles among those.&lt;/p&gt;

&lt;p&gt;He tested titles with ads and picked the one with the most clicks. He started with bad ideas, iterated, then picked the right one.&lt;/p&gt;

&lt;p&gt;A similar story with &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt; and &lt;em&gt;Choose Yourself.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;2&quot;&gt;#2.&lt;/h2&gt;

&lt;p&gt;Last year, I attended &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;a book writing workshop&lt;/a&gt; that drilled the value of bad ideas.&lt;/p&gt;

&lt;p&gt;One of the tasks was coming up with title ideas. One of the participants had an immigration practice helping people move to Australia. While he waited for the perfect title, I suggested &lt;em&gt;Aussie Job&lt;/em&gt; and follow-ups like &lt;em&gt;Aussie Colleges&lt;/em&gt; and &lt;em&gt;Aussie Marriages&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I don’t know which title he chose or if he even wrote the book. I just checked Amazon and there’s no &lt;em&gt;Aussie Job&lt;/em&gt;. &lt;em&gt;Insert shrugging emoji.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;a fan of 10-idea lists&lt;/a&gt;, that exercise was a piece of cake. It was just another daily prompt for ideas.&lt;/p&gt;

&lt;p&gt;That workshop pushed me to keep writing my 10 ideas daily.&lt;/p&gt;

&lt;h2 id=&quot;3&quot;&gt;3.&lt;/h2&gt;

&lt;p&gt;Don’t wait for a perfect book title, blog post subject, or business idea.&lt;/p&gt;

&lt;p&gt;Chasing the “perfect” idea leaves you blocked, waiting for inspiration. Aim for &lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;10 guilt-free bad ideas&lt;/a&gt;. Among those you’ll find a decent one that leads you to the right idea.&lt;/p&gt;

&lt;p&gt;Put your work out there. &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show it&lt;/a&gt;. If people like it, they will engage with it and remember it. Otherwise, they won’t.&lt;/p&gt;

&lt;p&gt;I’ve published over 600 posts over the years (half of them reposted on Medium and dev.to). Not every single post is a hit. Good ones stand out. People like, comment, and share them. Others go without “fame or glory.”&lt;/p&gt;

&lt;p&gt;Then the work is to have more bad ideas, find the least bad, and share them. Rinse and repeat.&lt;/p&gt;

&lt;p&gt;Sharif Shameem shared &lt;a href=&quot;https://sharif.io/looking-stupid&quot;&gt;a similar idea&lt;/a&gt;. He calls it, Aadil’s Law, named after a friend:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The amount of stupidity you’re willing to tolerate is directly proportional to the quality of ideas you’ll eventually produce&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Be willing to look stupid. Write 10 bad ideas every day and let the good ones emerge.&lt;/p&gt;

&lt;p&gt;Writing 10 ideas every day has been so valuable that I made it part of &lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=secret-generating-good-ideas&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;&lt;/em&gt; Write them daily and watch your life change.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We&apos;re Helping AI and Robots Replace Us</title>
   <link href="https://canro91.github.io/2026/03/13/Resistance/"/>
   <updated>2026-03-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/13/Resistance</id>
   <content type="html">&lt;p&gt;Take public transportation at rush hour. You’ll notice a clear pattern.&lt;/p&gt;

&lt;p&gt;More than half the people are heads down, headphones on, scrolling.&lt;/p&gt;

&lt;p&gt;If we don’t take care of our health, we’ll be depressed, sleep-deprived, deaf, people with the attention span of a fish.&lt;/p&gt;

&lt;p&gt;Taking care of our health is &lt;a href=&quot;/2025/09/23/Reinvention/&quot;&gt;the first step towards reinvention&lt;/a&gt;. After commuting, scrolling, junk food, and poor sleep, we lack the energy and drive to be creative and &lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;have new ideas&lt;/a&gt;. That’s when AI will eat us alive, when we’re too drained to imagine and create.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2026/03/09/ReadingRules/&quot;&gt;Reading&lt;/a&gt;, &lt;a href=&quot;/2026/01/21/GlucoseSpikes/&quot;&gt;eating healthy foods&lt;/a&gt;, and &lt;a href=&quot;/2026/03/11/Sleep/&quot;&gt;sleeping well&lt;/a&gt; isn’t just self-care. It’s an act of resistance.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: AI engineers, prompt injection, and job market</title>
   <link href="https://canro91.github.io/2026/03/13/FridayLinks/"/>
   <updated>2026-03-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/13/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 5 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; &lt;a href=&quot;https://yasint.dev/we-might-all-be-ai-engineers-now/&quot;&gt;We all might become AI engineers&lt;/a&gt; (6min), but we still need to know what to build and how it should work. AI needs hands on the wheel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; AI speeds up coding, but &lt;a href=&quot;https://danq.me/2026/03/06/writing-code-is-not-the-bottleneck/&quot;&gt;there are plenty of coding activities that aren’t typing&lt;/a&gt; (2min). There’s still work for humans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Last week, another npm package was infected. The interesting part? Someone stole the npm token by &lt;a href=&quot;https://grith.ai/blog/clinejection-when-your-ai-tool-installs-another&quot;&gt;injecting a prompt into a GitHub issue&lt;/a&gt; (10min). SQL injection isn’t the #1 vulnerability anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; If you’re a C# coder using Dapper, be aware of &lt;a href=&quot;https://consultwithgriff.com/dapper-nvarchar-implicit-conversion-performance-trap&quot;&gt;data type conversions that might be slowing down your queries&lt;/a&gt; (8min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5.&lt;/strong&gt; If you’ve had a hard time finding a job. You’re not alone. &lt;a href=&quot;https://x.com/JosephPolitano/status/2029916364664611242&quot;&gt;Hiring is as bad as during the pandemic&lt;/a&gt; (1min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Last week, while migrating a legacy app, I wrote about my adventures with &lt;a href=&quot;/2026/03/11/OneToOne/&quot;&gt;Entity Framework Core joining tables&lt;/a&gt; (5min) and with &lt;a href=&quot;/2026/03/09/Summernote/&quot;&gt;Blazor building a component for a HTML editor&lt;/a&gt; (4min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-ai-engineers-prompt-injection-job-market&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. This book shares 10 small daily ideas that create big change. I adopted most of them while recovering from burnout&lt;/p&gt;

&lt;p&gt;If you’re ready to start small and see big change, check it out.&lt;/p&gt;

&lt;p&gt;Until next Friday. Stay coding smartly!&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The #1 Health Tip From the Guy Obsessed With Living Forever</title>
   <link href="https://canro91.github.io/2026/03/11/Sleep/"/>
   <updated>2026-03-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/11/Sleep</id>
   <content type="html">&lt;p&gt;If you could only do one thing for your health, it should be sleep.&lt;/p&gt;

&lt;p&gt;Brian Johnson, &lt;a href=&quot;/2025/09/14/LivingForever/&quot;&gt;the guy trying to live forever&lt;/a&gt;, teaches in his videos and interviews to be &lt;em&gt;a professional sleeper&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;He’s right when he says nobody teaches us to sleep.&lt;/p&gt;

&lt;p&gt;For better sleep, build your life around sleep:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Have dinner earlier&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;Remove screens before bed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Adjust your room temperature&lt;/li&gt;
  &lt;li&gt;Turn off lights in your room&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beyond the basics, Brian’s recent videos taught me to aim for a lower heart rate before sleep. Try journaling, taking deep breaths, or meditation. Slow down before going to bed.&lt;/p&gt;

&lt;p&gt;Staying late for my writing streak taught me &lt;a href=&quot;/2025/07/08/TheOnlyProductivityTip/&quot;&gt;rest is the best productivity hack&lt;/a&gt;. Coffee can’t fix a bad night of sleep. So go to sleep!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to Set Up a Unilateral One-to-One Relationship with Entity Framework Core</title>
   <link href="https://canro91.github.io/2026/03/11/OneToOne/"/>
   <updated>2026-03-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/11/OneToOne</id>
   <content type="html">&lt;p&gt;In another episode of &lt;em&gt;Adventures with Entity Framework while migrating a legacy app…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Entity Framework Core only populated a child entity on one item in a result. To honor &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;the 20-minute rule&lt;/a&gt;, and for my future self, here’s what I found:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; You don’t need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithOne()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasForeignKey()&lt;/code&gt; when configuring the relationship.&lt;/p&gt;

&lt;h2 id=&quot;1-lets-create-an-optional-one-to-one-relationship&quot;&gt;#1. Let’s create an optional one-to-one relationship.&lt;/h2&gt;

&lt;p&gt;Let’s create a Movie and an Award table.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Awards&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AwardId&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* &amp;lt;-- Optional. No FK here */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since the relationship is optional, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AwardId&lt;/code&gt; is nullable. This type dictates &lt;a href=&quot;/2026/01/13/NullableJoiningProperty/&quot;&gt;what JOIN Entity Framework Core uses&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-configure-the-one-to-one-relationship&quot;&gt;#2. Configure the one-to-one relationship&lt;/h2&gt;

&lt;p&gt;Let’s configure the relationship using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasOne()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithOne()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LookMaWhatEntityFrameworkDoes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Award&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AwardId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//       ^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Optional&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnModelCreating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modelBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;modelBuilder&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AwardId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnDelete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeleteBehavior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Restrict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Awards&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To &lt;a href=&quot;/2026/02/12/RealSkills/&quot;&gt;verify Copilot’s answer&lt;/a&gt;, I went through &lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/modeling/relationships/one-to-one&quot;&gt;the official docs here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-retrieve-movies-and-their-awards&quot;&gt;#3. Retrieve movies and their awards&lt;/h2&gt;

&lt;p&gt;And now, let’s create an award, add two movies, and retrieve them to check their awards.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LookMaWhatEntityFrameworkDoes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AllAwardsPlease&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Server=(localdb)\\MSSQLLocalDB;Database=Movies;Trusted_Connection=True;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContextOptionsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseSqlServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 1. Let&apos;s create an &quot;Oscar&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Awards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Oscar&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 2. Let&apos;s create two movies that have won an &quot;Oscar&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oscar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Awards&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefaultAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oscar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Forrest Gump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;AwardId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oscar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;AwardId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oscar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 3. Let&apos;s retrieve all movies, expecting to have an Award&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
                                &lt;span class=&quot;c1&quot;&gt;// Imagine more filters here			&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                                &lt;span class=&quot;c1&quot;&gt;// Yes, I&apos;m including it here&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToListAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Assert.IsNotNull failed.&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Play sad trumpet sound.&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sorry for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; inside the Assert. &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;That’s not a good idea&lt;/a&gt;. But I’m lazy and I’m taking too long writing this.&lt;/p&gt;

&lt;p&gt;Yes, it fails. Play sad trumpet, please. The second movie’s award isn’t populated. Arrggg!&lt;/p&gt;

&lt;p&gt;My fault!&lt;/p&gt;

&lt;h2 id=&quot;4-ignore-withone&quot;&gt;#4. Ignore WithOne()&lt;/h2&gt;

&lt;p&gt;Since I’m setting an unidirectional relationship, one movie/one award/multiple movies, only configuring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasOne()&lt;/code&gt; is enough.&lt;/p&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithOne()&lt;/code&gt; was telling Entity Framework Core that one award could only belong to one movie. And that’s not the case here.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnModelCreating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modelBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Remove the entire configuration&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Or,		&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;modelBuilder&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Just this&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Award&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Awards&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And since I’m following default naming conventions, I could even delete the configuration. Ahh, Cascade sets to null, which is fine here.&lt;/p&gt;

&lt;p&gt;Yes, the right answer is to do nothing.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;For debugging and problem-solving tips, read &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=set-unilateral-one-one-relationship-entity-framework-core&quot;&gt;Street-Smart Coding&lt;/a&gt;. Those two skills are more relevant now in the era of AI-assisted coding.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building a Reusable Blazor Component for Summernote</title>
   <link href="https://canro91.github.io/2026/03/09/Summernote/"/>
   <updated>2026-03-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/09/Summernote</id>
   <content type="html">&lt;p&gt;&lt;em&gt;In another adventure with Blazor…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While migrating a legacy app, I had to “componentize” an HTML editor. It used &lt;a href=&quot;https://summernote.org/getting-started/&quot;&gt;Summernote&lt;/a&gt;, &lt;em&gt;“a super simple WYSIWYG editor on Bootstrap”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The fun part was learning JavaScript interop: &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-10.0#invoke-js-functions&quot;&gt;calling JavaScript from .NET&lt;/a&gt; and &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-10.0#pass-a-dotnetobjectreference-to-an-individual-javascript-function&quot;&gt;viceversa&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After some Googling and sneaking into abandoned GitHub repos, here’s what I came up with:&lt;/p&gt;

&lt;h2 id=&quot;the-component&quot;&gt;The component&lt;/h2&gt;

&lt;p&gt;In Summernote.razor:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;@using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Microsoft&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AspNetCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sections&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HeadContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;css/summernote.css&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HeadContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MarkupString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SectionContent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SectionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;js/summernote.js&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SectionContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In Summernote.razor.cs:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.JSInterop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyCoolComponents.HtmlEditor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Summernote&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAsyncDisposable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;summernote_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NewGuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IJSObjectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DotNetObjectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Summernote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dotnetRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_editorInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueChanged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IJSRuntime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsRuntime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnAfterRenderAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstRender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_editorInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_dotnetRef&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DotNetObjectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsRuntime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IJSObjectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;import&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;./PutYourPathHere/Summernote.razor.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeVoidAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;edit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_dotnetRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OnTextChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_editorInitialized&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JSInvokable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnTextChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;editorText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;editorText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvokeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;editorText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueTask&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DisposeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_module&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DisposeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_dotnetRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s where the magic happens.&lt;/p&gt;

&lt;p&gt;After rendering the component, it calls a JavaScript function that initializes the Summernote editor. Then, it registers a callback that calls a .NET function every time the editor changes to update the component state.&lt;/p&gt;

&lt;p&gt;Notice it stores content in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MarkupString&lt;/code&gt; but it binds as a string.&lt;/p&gt;

&lt;p&gt;In Summernote.razor.js:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dotnetRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;snid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;snid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;summernote&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;callbacks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$editable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;dotnetRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;invokeMethodAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it&lt;/h2&gt;

&lt;p&gt;And here’s a sample form using the editor:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;@using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyCoolComponents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HtmlEditor&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditForm&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyCoolRequest&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OnValidSubmit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OnValidSubmit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataAnnotationsValidator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValidationSummary&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;
&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;Content&amp;lt;/label&amp;gt;
&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Summernote&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Annotation.Content&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;^^^^^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;
            &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Look&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;works&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValidationMessage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;() =&amp;gt; Annotation.Content&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;feedback&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; /&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;btn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;primary&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;Submit&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This piece of code is &lt;em&gt;provided “as is”, without warranty of any kind&lt;/em&gt;…Blah, blah, blah. Use under your own risk. Steal it.&lt;/p&gt;

&lt;p&gt;If this helped you, you’ll love &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=blazor-component-summernote&quot;&gt;Street-Smart Coding&lt;/a&gt;—30 proven lessons to help you level up your coding journey.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ten Reading Rules to Read More—and Love It</title>
   <link href="https://canro91.github.io/2026/03/09/ReadingRules/"/>
   <updated>2026-03-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/09/ReadingRules</id>
   <content type="html">&lt;p&gt;Inspired by &lt;a href=&quot;/2025/09/08/StoicBookReading/&quot;&gt;Ryan Holiday’s reading rules&lt;/a&gt;, I’ve decided to come up with my own to truly enjoy reading.&lt;/p&gt;

&lt;p&gt;I’ve had to change my mind about books. From chasing a book count to reading for action and pleasure.&lt;/p&gt;

&lt;p&gt;Here are my 10 reading rules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Always be reading something.&lt;/strong&gt; At least one page a day. Like &lt;a href=&quot;/2024/12/10/JimKwik/&quot;&gt;Jim Kwik says&lt;/a&gt;, reading is like exercise for the brain. And when scrolling and distractions are the norm, reading is a revolution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Reading over phone time and social media.&lt;/strong&gt; To &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;reduce my phone time&lt;/a&gt;, I use books as conscious replacements. I leave books where I usually leave my phone to break the habit of scrolling.&lt;/p&gt;

&lt;p&gt;And after &lt;a href=&quot;/2026/02/24/NoSocialMediaWeek/&quot;&gt;a week off social media&lt;/a&gt;, I now read first thing in the morning instead of checking email or social media.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Read two books at once.&lt;/strong&gt; Balance non-fiction and fiction books. Non-fiction starts my mornings and fiction helps me unwind as night.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Write a book recommendation.&lt;/strong&gt; While reading a book, write a list of all books mentioned in the book. This is like climbing the inspiration tree as Austen Kleon says in &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Read actively.&lt;/strong&gt; Write in margins, fold corners, underline, highlight. &lt;a href=&quot;/2026/01/20/WritingInBooks/&quot;&gt;Your book marks can become a legacy&lt;/a&gt;. A book without marks is a book you haven’t read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Write a 10-idea list.&lt;/strong&gt; I’m a big fan of &lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;10-idea lists&lt;/a&gt;. Instead of note-taking apps, I write 10 ideas that resonate after finishing a book. It forces me to recall and remember what I read. Bonus point when paired with &lt;a href=&quot;/2026/01/25/7Words/&quot;&gt;a 7-word summary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Reread.&lt;/strong&gt; I’ve had to &lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;reconcile myself with rereading&lt;/a&gt;. It used to be a sin when I wanted to hit a large book count. The goal is to gain insight, not just hit a book number. Rereading is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Prefer physical books.&lt;/strong&gt; They give you spatial clues to remember information. You might remember that an important concept is on a left-hand page, near the bottom. eBooks can’t beat that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. No shame in piling books.&lt;/strong&gt; From &lt;em&gt;How to Be Rich&lt;/em&gt; by Ramit Seti, I learned we shouldn’t cut on things we love. I’m not cutting on books.&lt;/p&gt;

&lt;p&gt;You’re the books you’ve read. And those you haven’t read yet. Just don’t let your books become “shelve-help.” Build a pile, go through it, then build another one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Binge-read your favorite writer.&lt;/strong&gt; I did it when I found &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt; for the first time. It shows you how their voice change and how your perspective expands.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My February Book Experiment Is Already A Success—But Still in Progress</title>
   <link href="https://canro91.github.io/2026/03/07/BookExperiment/"/>
   <updated>2026-03-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/07/BookExperiment</id>
   <content type="html">&lt;p&gt;Last February, I decided to &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;write another book&lt;/a&gt; but in a completely different way.&lt;/p&gt;

&lt;p&gt;I started from a validated idea. I turned &lt;a href=&quot;/2025/07/03/LifeChangingIdeas/&quot;&gt;my most popular personal development post&lt;/a&gt;, into &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;a mini book&lt;/a&gt;. Instead of starting with the first words, I began with the outline, title, sales page, and cover. I wrote it backwards.&lt;/p&gt;

&lt;p&gt;Here’s the final result: &lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=my-february-book-experiment-success&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;My success metric was a single sale.&lt;/p&gt;

&lt;p&gt;Yesterday, I announced my book in &lt;a href=&quot;/2026/03/06/FridayLinks/&quot;&gt;my Friday Links email&lt;/a&gt; and one reader bought it. A small victory dance.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2026-03-07-BookExperiment/FirstSale.png&quot; alt=&quot;First sale of 10 Surprisingly Simple Ideas That Changed My Life&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;First sale&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;With that milestone reached, here’s how I’m continuing the experiment:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Reach out to my “advance readers” team to get my first review on Amazon.&lt;/li&gt;
  &lt;li&gt;Promote my book via a book promotion email list for more reach.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This experiment will shape my launching strategy for future books. The real works begins after the writing is done.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Life changes, leaving Google, and lone wolves</title>
   <link href="https://canro91.github.io/2026/03/06/FridayLinks/"/>
   <updated>2026-03-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/06/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there!&lt;/p&gt;

&lt;p&gt;Burnout hit me hard in 2023. It changed everything.&lt;/p&gt;

&lt;p&gt;It hurt both my mind and my body. I was at my best coding job. Then, in 2024, I was laid off. Perfect combo, right?&lt;/p&gt;

&lt;p&gt;The way up was slow. Master plans didn’t work. Getting up felt impossible.&lt;/p&gt;

&lt;p&gt;One quote inspired me to get up: &lt;em&gt;“If you’re lost, start with your health.”&lt;/em&gt; That became my first step.&lt;/p&gt;

&lt;p&gt;Small daily actions made change possible.&lt;/p&gt;

&lt;p&gt;That journey led me to write &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=life-changes-leaving-google-lone-wolves&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. I’m launching it this week.&lt;/p&gt;

&lt;p&gt;This isn’t a 7-day program or a magic formula. It’s about small daily ideas that create big change.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And as usual, here are 4 stories that resonated with me this week:&lt;/p&gt;

&lt;p&gt;#1. I don’t use Google as my default search engine, but I still use it for email. For digital hygiene or simply for boycotting big corporations, &lt;a href=&quot;https://pseudosingleton.com/leaving-google-improved-my-life/&quot;&gt;leaving Google may improve your digital life too&lt;/a&gt; (7min)&lt;/p&gt;

&lt;p&gt;#2. Hiring sucks. CVs, interviews, recruiters…But the best way to think of hiring is a two-way sales negotiation. If you’re looking for a job, here’s &lt;a href=&quot;https://zeldman.com/2026/03/02/advice-for-job-seekers/&quot;&gt;a piece of advice you shouldn’t miss&lt;/a&gt; (5min)&lt;/p&gt;

&lt;p&gt;#3. AI is changing our job descriptions… and killing the need for junior coders. AI is splitting the dev population into &lt;a href=&quot;https://www.jeffgeerling.com/blog/2026/expert-beginners-and-lone-wolves-dominate-llm-era/&quot;&gt;expert beginners and lone wolves&lt;/a&gt; (8min)&lt;/p&gt;

&lt;p&gt;#4. The next time I’m asked what superpower I’d like to have, I’d say talk to anyone in any situation. Here’s a good story from &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/2026/feb/24/stranger-secret-how-to-talk-to-anyone-why-you-should&quot;&gt;The Guardian&lt;/a&gt; (14min) about it.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Thanks to the internet, a February post got traction last week. In case you missed it, here are &lt;a href=&quot;/2026/02/06/AIStats/&quot;&gt;the numbers that deflate the AI hype about replacing coders—and ease FOMO&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;em&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too.&lt;/em&gt; If you’re ready to start small and see big change, check it out.&lt;/p&gt;

&lt;p&gt;Until next Friday. Keep coding smartly!&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What to Do to Thrive in the AI Era</title>
   <link href="https://canro91.github.io/2026/03/05/AIProofed/"/>
   <updated>2026-03-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/05/AIProofed</id>
   <content type="html">&lt;p&gt;Whether you see it as a tool or a breakthrough, AI is changing jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/11/23/TranslatorsVsAI/&quot;&gt;Translators are panicking&lt;/a&gt;. Some coders brag about “coding” from their phones. Every new tool and model fuels &lt;a href=&quot;/2025/12/28/AIFomo/&quot;&gt;AI FOMO&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2026/02/06/AIStats/&quot;&gt;Real numbers about AI adoption&lt;/a&gt; tell one story. &lt;a href=&quot;/2025/10/15/AIRealReason/&quot;&gt;CEOs tell another&lt;/a&gt;. Maybe we’re all doomed. Predictions are hard. I hope I wasn’t wrong with &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;mine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are 2 ideas about how to AI-proof yourself:&lt;/p&gt;

&lt;h2 id=&quot;1-learn-to-learn&quot;&gt;#1. Learn to learn&lt;/h2&gt;

&lt;p&gt;From &lt;a href=&quot;/2025/08/21/21Lessons/&quot;&gt;21 Lessons for the 21st Century&lt;/a&gt;, the only skill to learn is the ability to learn new things.&lt;/p&gt;

&lt;p&gt;The only certain thing is change. And being able to adapt is crucial. Pick new skills you enjoy. Bonus points if they earn money.&lt;/p&gt;

&lt;h2 id=&quot;2-literacy-and-health&quot;&gt;#2. Literacy and health&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2026/02/22/Papyrus/&quot;&gt;Papyrus&lt;/a&gt; made me think about the future.&lt;/p&gt;

&lt;p&gt;It’s a book about the invention of books in the ancient world. But the past teaches us about the future.&lt;/p&gt;

&lt;p&gt;In ancient Greece, free men learned to read, talk, and think, and took care of their bodies. Manual labor was for slaves. Of course, that wasn’t a good thing.&lt;/p&gt;

&lt;p&gt;If AI takes over repetitive work, as slaves once did in ancient Greece, we’ll be like free to focus on creativity and growth.&lt;/p&gt;

&lt;p&gt;Maybe AI will kill most knowledge jobs, because it thrives on tasks humans do and hiring people may no longer make economic sense.&lt;/p&gt;

&lt;p&gt;Until then, while waiting for universal basic income or or something darker, I’m doubling down on writing and chasing my curiosity—simply because I enjoy it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Compiled The 10 Simple Ideas That *Truly* Changed My Life Into A Book</title>
   <link href="https://canro91.github.io/2026/03/04/10SimpleIdeas/"/>
   <updated>2026-03-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/04/10SimpleIdeas</id>
   <content type="html">
&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2026-03-04-10SimpleIdeas/Cover.png&quot; alt=&quot;10 Surprisingly Simple Ideas That Changed My Life cover&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Big plans rarely change your life.&lt;/p&gt;

&lt;p&gt;In 2023, life forced me to change. Burnout hit me hard. It hurt both my mind and my body. My poor eating habits only made things worse. Then, in 2024, I was laid off. The last nail in the coffin. It felt devastating.&lt;/p&gt;

&lt;p&gt;The way up was slow. Master plans and New Year’s resolutions didn’t work. A new life felt impossible.&lt;/p&gt;

&lt;p&gt;One single quote inspired me to get up: “If you’re lost, start with your health.” That quote became the foundation of Chapter 1 and the real step toward change.&lt;/p&gt;

&lt;p&gt;Small daily actions made real change possible.&lt;/p&gt;

&lt;p&gt;That’s why I wrote &lt;em&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too.&lt;/em&gt; If you’ve ever asked, &lt;em&gt;“How do you actually change your life”&lt;/em&gt;, this is for you.&lt;/p&gt;

&lt;h2 id=&quot;big-plans-dont-change-your-life-small-daily-actions-do&quot;&gt;Big plans don’t change your life. Small daily actions do.&lt;/h2&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2026-03-04-10SimpleIdeas/ScrollingThru.gif&quot; alt=&quot;Scrolling through the first pages of 10 Surprisingly Simple Ideas That Changed My Life&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Preview of the first ~10 pages&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Forget passion and master plans. This book is about the tiny daily actions that actually change your life. I know because they changed mine.&lt;/p&gt;

&lt;p&gt;Inside this book, you’ll find 10 surprisingly simple ideas that changed my life, and can change yours too:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Idea #1: The mindset shift that gave me purpose when I couldn’t get out of bed.&lt;/li&gt;
  &lt;li&gt;Idea #2: The daily habit that keeps burnout away.&lt;/li&gt;
  &lt;li&gt;Idea #3: The productivity hack that doubled my focus.&lt;/li&gt;
  &lt;li&gt;Idea #4: The free advice that saved me during my hardest year.&lt;/li&gt;
  &lt;li&gt;Idea #5: The creative practice that helped me write books and countless articles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and five more life-changing ideas you can start using right away.&lt;/p&gt;

&lt;p&gt;This isn’t a 7-day program or a magic formula. It’s about small daily ideas that create big change.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=simple-ideas-truly-changed-life-book&quot;&gt;Get your copy here&lt;/a&gt;—For launch week only: You can even just pay $1. Begin your transformation today.&lt;/p&gt;

&lt;p&gt;Start small. Change big.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;PS: Also available on &lt;a href=&quot;https://www.amazon.com/dp/B0GQVY2W3D&quot;&gt;Kindle and paperback&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Five Changes I&apos;d Like To Make On My Blog</title>
   <link href="https://canro91.github.io/2026/03/03/Tweaks/"/>
   <updated>2026-03-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/03/Tweaks</id>
   <content type="html">&lt;p&gt;To keep myself accountable, here’s a list of changes I’d like to make on my blog:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. New About copy.&lt;/strong&gt; I still have the one from the days I was looking for a full-time job as a coder. That’s not the case anymore. These days, I’m a digital writer with a part-time coding job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. More recent “Start Here.”&lt;/strong&gt; The “Start Here,” on my &lt;a href=&quot;/about&quot;&gt;About&lt;/a&gt;, dates back to my full-time coding days, with only tutorials. Google and AI have killed tutorials. And I’m not writing as many coding tutorials anymore.&lt;/p&gt;

&lt;p&gt;With metrics from other platforms, &lt;a href=&quot;/2024/12/15/BloggingWorkflow/&quot;&gt;I follow POSSE&lt;/a&gt;, it’s time to update it with my most liked content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. New tags.&lt;/strong&gt; This started as a coding blog. Five years ago, &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;my first post&lt;/a&gt; was a coding tutorial. Over time, it became my small corner, where I write about almost anything.&lt;/p&gt;

&lt;p&gt;In 2024, when I started &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;my daily writing challenge&lt;/a&gt;, I created a &lt;a href=&quot;/tags/misc&quot;&gt;/misc&lt;/a&gt; tag. But, naturally some patterns have arisen,&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Health&lt;/li&gt;
  &lt;li&gt;Experiments&lt;/li&gt;
  &lt;li&gt;Book writing&lt;/li&gt;
  &lt;li&gt;Random thoughts&lt;/li&gt;
  &lt;li&gt;Personal development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;#4. Book page.&lt;/strong&gt; On the left sidebar, I feature &lt;a href=&quot;/projects&quot;&gt;my projects&lt;/a&gt; and open source contributions. It’s time to feature my books instead.&lt;/p&gt;

&lt;p&gt;In the last year, I updated &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=five-changes-id-like-make-blog&quot;&gt;Start Testing&lt;/a&gt; and launched &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=five-changes-id-like-make-blog&quot;&gt;Street-Smart Coding&lt;/a&gt;. And I’m about to finish &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;another book experiment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. On this date widget.&lt;/strong&gt; Sometimes I’ve written “On This Date” posts, like &lt;a href=&quot;/2025/10/05/AroundThisDate/&quot;&gt;this one&lt;/a&gt; and &lt;a href=&quot;/2025/02/12/AroundThisDay/&quot;&gt;this one&lt;/a&gt;. I write about the posts from past years written on the same date. I’d like to create a small widget to automate that. Under every post show 2 posts from previous and following years.&lt;/p&gt;

&lt;p&gt;That’s one of &lt;a href=&quot;/2025/05/02/Blogging/&quot;&gt;the best parts of having a blog&lt;/a&gt;: you can tweak it however you want. Try beating that, social platforms!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update (Mar 2026):&lt;/strong&gt;&lt;/em&gt; Here are the changes I’ve made so far:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I updated the Start here section on &lt;a href=&quot;/about&quot;&gt;my about&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;I created these tags: &lt;a href=&quot;/tags/wellness&quot;&gt;wellness&lt;/a&gt;, &lt;a href=&quot;/tags/selfgrowth&quot;&gt;self growth&lt;/a&gt;, &lt;a href=&quot;/tags/bookwriting&quot;&gt;book writing&lt;/a&gt;, &lt;a href=&quot;/tags/experiments&quot;&gt;experiments&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;I wrote a &lt;a href=&quot;/books&quot;&gt;books page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update (Apr 2026):&lt;/strong&gt;&lt;/em&gt; More changes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I made the left bar scrollable.&lt;/li&gt;
  &lt;li&gt;I tweaked &lt;a href=&quot;/&quot;&gt;my homepage&lt;/a&gt; and &lt;a href=&quot;/about&quot;&gt;my about&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Five Simple Blogging Rules for Consistency</title>
   <link href="https://canro91.github.io/2026/03/03/BloggingRules/"/>
   <updated>2026-03-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/03/BloggingRules</id>
   <content type="html">&lt;p&gt;This blog started as a coding notebook, but now it’s my small corner of the web.&lt;/p&gt;

&lt;p&gt;I’ve changed what a post is. It used to be &lt;a href=&quot;/tags/tutorial/&quot;&gt;tutorials&lt;/a&gt;. I spent time designing covers with Canva and picking images from Unsplash. I wanted it to look like a “real” blog. Those days, around 2022, I shared my posts on LinkedIn to boost traffic and &lt;a href=&quot;/2025/06/16/BloggingExpectations/&quot;&gt;get attention&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But &lt;a href=&quot;/2025/12/06/400DailyPosts/&quot;&gt;writing daily posts&lt;/a&gt; left no time for covers and images. To stay consistent, I simplified my approach and adopted some rules. I first shared them &lt;a href=&quot;/2024/11/13/BlogMore/&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;/2025/04/14/TwoBloggingTips/&quot;&gt;here&lt;/a&gt;, but now I’m updating them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Write short pieces.&lt;/strong&gt; You don’t have to write breakdowns or 2,000-word posts. If it’s longer than a tweet, publish it. &lt;a href=&quot;/2025/07/15/Headlines/&quot;&gt;A good headline&lt;/a&gt; plus some sentences works. &lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;A 10-idea list&lt;/a&gt; works. Some random thoughts are worth publishing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Write reaction posts instead of comments.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/11/09/LimitedKeystrokes/&quot;&gt;Preserve your keystrokes&lt;/a&gt; by replying on a post.&lt;/li&gt;
  &lt;li&gt;Title your reaction post with “re:”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;#3. Make your urls easy to pronounce.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. If you miss a day, write two posts the next day.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Use your posts multiple times.&lt;/strong&gt; Repost them on another platform. Use them as book chapters. Share shorter versions on social media. You can steal from yourself.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Writing Devices From Slow Horses, Season 5</title>
   <link href="https://canro91.github.io/2026/03/01/SlowHorses/"/>
   <updated>2026-03-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/01/SlowHorses</id>
   <content type="html">&lt;p&gt;TV shows are a good writing classes.&lt;/p&gt;

&lt;p&gt;These days, I binge-watched Slow Horses Season 5. Not that I’m a fan of spy shows.&lt;/p&gt;

&lt;p&gt;After watching one of the first episodes, I noticed &lt;a href=&quot;/2026/02/25/SlowHorses/&quot;&gt;how good episode endings it has&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I watched the rest of the season with my writer’s glasses on, here are some of the devices I noticed—No spoilers:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Increasing tension in every episode. The enemy is using the same strategy the Brits used in them during the Cold War. Each episode follows one of the steps.&lt;/li&gt;
  &lt;li&gt;Plot twist. The innocent turns out to be not that innocent.&lt;/li&gt;
  &lt;li&gt;Ending episode with revelation. One episode ends without any dialog or action scene, but with a text message.&lt;/li&gt;
  &lt;li&gt;Connecting elements. A box full of souvenirs and a tape recorders show up in screen, connecting the plot between episodes.&lt;/li&gt;
  &lt;li&gt;The season ends connecting with the first episode. One of the protagonists makes a phone call, following up a conversation from the first episode.&lt;/li&gt;
  &lt;li&gt;Make you hate a character. That’s not the villain, but a protagonist used as an “useful idiot.” You just hate it by the end of the season. Give characters some life.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More TV show breakdowns: &lt;a href=&quot;/2025/09/27/BlackDoves/&quot;&gt;Black Doves&lt;/a&gt;, &lt;a href=&quot;/2025/07/28/Chespirito/&quot;&gt;Not Really on Purpose&lt;/a&gt;, and &lt;a href=&quot;/2025/05/18/HouseMD/&quot;&gt;House M.D.&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Sometimes You Just Need to Be A Collector</title>
   <link href="https://canro91.github.io/2026/03/01/Collector/"/>
   <updated>2026-03-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/03/01/Collector</id>
   <content type="html">&lt;p&gt;For &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;my February book experiment&lt;/a&gt;, I wanted to open each chapter with a powerful quote. Stealing from &lt;a href=&quot;/2026/01/03/Mastery/&quot;&gt;Mastery by Robert Greene&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But then I realized I didn’t have enough quotes. I needed at least ten. I didn’t want to Google or ChatGPT for quotes and get the same quotes every motivational post uses. It was too late to read or surf the web looking for quotes.&lt;/p&gt;

&lt;p&gt;That’s when I wish I had been a quote collector.&lt;/p&gt;

&lt;p&gt;I remember Austen Kleon, author of &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt; and &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt;, who has a notebook for quotes and then he transcribe them.&lt;/p&gt;

&lt;p&gt;I wish I had joined &lt;a href=&quot;/2026/01/29/RandomThoughts/&quot;&gt;the notebook cult&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: AI, LinkedIn, and the missing CS semester</title>
   <link href="https://canro91.github.io/2026/02/27/FridayLinks/"/>
   <updated>2026-02-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/27/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 thought-provoking links I found this week. Plus my reflections after a week off social media.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; AI makes code cheaper, but cheap code means not understanding what you’re building. “Use AI or stay behind” shouldn’t be the real mantra. It should be &lt;a href=&quot;https://codemanship.wordpress.com/2026/02/21/is-comprehension-debt-in-your-risk-register/&quot;&gt;rely on AI and get left behind&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; College and bootcamps don’t teach the skills you need for real-world coding. That’s why I wrote &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Street-Smart Coding&lt;/a&gt;, by the way. This week, I found an updated version of &lt;a href=&quot;https://missing.csail.mit.edu/&quot;&gt;The Missing Semester&lt;/a&gt;, a free course covering text editing, version control, agentic coding, and other skills nobody else teaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Do you want to see a blue shield on your LinkedIn profile? Think twice after seeing &lt;a href=&quot;https://thelocalstack.eu/posts/linkedin-identity-verification-privacy/&quot;&gt;all the data LinkedIn and friends collect to verify us&lt;/a&gt; (12min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Now with cheaper code it’s finally time to tackle &lt;a href=&quot;https://codemanship.wordpress.com/2026/02/23/will-you-finally-address-your-development-bottlenecks-in-2026/&quot;&gt;the real bottlenecks&lt;/a&gt; (3min). And no, coding and typing were never the bottleneck.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I spent last week away from social media, and it turns out &lt;a href=&quot;/2026/02/24/NoSocialMediaWeek/&quot;&gt;I’m another dopamine junkie&lt;/a&gt; (4min), even with mindful use and a timer. Arrggg!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;em&gt;Street-Smart Coding,&lt;/em&gt; 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until next Friday. Keep coding smartly&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>BugOfTheDay: Entity Framework Core And SQL Server Have Different Takes On Truncating Strings</title>
   <link href="https://canro91.github.io/2026/02/26/Truncating/"/>
   <updated>2026-02-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/26/Truncating</id>
   <content type="html">&lt;p&gt;A subtle detail easy to forget when working with legacy applications that use stored procedures.&lt;/p&gt;

&lt;h2 id=&quot;stored-procedures-swallow-long-strings&quot;&gt;Stored procedures swallow long strings&lt;/h2&gt;

&lt;p&gt;If you’re inserting a long string into a shorter column, SQL Server truncates the parameters with no complaints,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* &amp;lt;-- A 10 here */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InsertMovie&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* &amp;lt;-- Notice the &quot;10&quot; here */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* @Name is a NVARCHAR(10), but the value is longer than 10 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;EXEC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InsertMovie&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;ThisMovieNameIsWayTooLongForTheColumn&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The stored procedure “swallowed” that long parameter. No complaints! But wait to see what Entity Framework does.&lt;/p&gt;

&lt;h2 id=&quot;entity-framework-doesnt-validate-or-truncate&quot;&gt;Entity Framework doesn’t validate or truncate&lt;/h2&gt;

&lt;p&gt;Now, if we try to do the same inside a unit test,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LookMaWhatEntityFrameworkDoes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TruncateItPlease&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Server=(localdb)\\MSSQLLocalDB;Database=Movies;Trusted_Connection=True;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContextOptionsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseSqlServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;RememberThatNameInTheDBHasSize10&quot;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Test method LookMaWhatEntityFrameworkDoes.MovieTests.TruncateItPlease threw exception: &lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Microsoft.Data.SqlClient.SqlException: &lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// String or binary data would be truncated in table &apos;Movies.dbo.Movies&apos;, column &apos;Name&apos;. Truncated value: &apos;RememberTh&apos;.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Entity Framework Core blows in your face. At least, it tells you the table and column names, and the value being truncated.&lt;/p&gt;

&lt;p&gt;Another subtle detail worth remembering. Et voilà!&lt;/p&gt;

&lt;p&gt;Here’s another Entity Framework gotcha: &lt;a href=&quot;/2026/01/13/NullableJoiningProperty/&quot;&gt;Nullable foreign keys&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This TV Show Is a Masterclass On Episode Endings</title>
   <link href="https://canro91.github.io/2026/02/25/SlowHorses/"/>
   <updated>2026-02-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/25/SlowHorses</id>
   <content type="html">&lt;p&gt;TV show are a great source of writing devices.&lt;/p&gt;

&lt;p&gt;I’ve been binged watching &lt;em&gt;Slow Horses&lt;/em&gt;, season 5. An espionage series in present-day London about a team of problematic MI5 agents, relegated to a forgotten office.&lt;/p&gt;

&lt;p&gt;Even though, they’re labeled as poor performers—slow horses—they always end up saving the day.&lt;/p&gt;

&lt;p&gt;After watching the first three episodes of season 5, I realized each one has ended in a dramatic way.&lt;/p&gt;

&lt;p&gt;The second episode ends with a text message that makes you rush to click on “Next episode.” One of the protagonists, against all odds, is dating a pretty hot woman. Or that’s what he thinks. The text message reveals the girl’s true intention in a “wait, what” moment right at the end of the episode.&lt;/p&gt;

&lt;p&gt;It reminded me of one of &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;’s writing tricks: &lt;a href=&quot;/2025/05/09/BleedInTheFirstLine/&quot;&gt;start with blood or drama&lt;/a&gt;, end with a cliffhanger.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Spent a Week Without Social Media—And Realized I&apos;m Just Another Junkie</title>
   <link href="https://canro91.github.io/2026/02/24/NoSocialMediaWeek/"/>
   <updated>2026-02-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/24/NoSocialMediaWeek</id>
   <content type="html">&lt;p&gt;One LinkedIn post pushed me to quit social media for a week.&lt;/p&gt;

&lt;p&gt;I’ve been writing on LinkedIn (and other social blogs) for two years. And to “grow” my account as a creator, I’ve been following common advice: “be your own case study” and “engage after posting.” I questioned that advice, yet still spent an hour a day engaging.&lt;/p&gt;

&lt;p&gt;I had two wake-up calls:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Noticing my phone time. 2h18min on average the week before.&lt;/li&gt;
  &lt;li&gt;Finding a post inviting people to disappear from the Internet without apologies, announcements, or guilt. I did that—kind of.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a week, I stayed away from anything with a feed: Medium, LinkedIn, dev.to, YouTube, Reddit…But I still scheduled my weekly content for the platforms where I write.&lt;/p&gt;

&lt;h2 id=&quot;you-need-an-intention-and-a-replacement&quot;&gt;You need an intention and a replacement&lt;/h2&gt;

&lt;p&gt;Ignoring &lt;a href=&quot;/2025/07/16/ProductivityTips/&quot;&gt;my own productivity advice&lt;/a&gt;, I checked my email and socials before work.&lt;/p&gt;

&lt;p&gt;When &lt;a href=&quot;/2026/02/15/NoFeeds/&quot;&gt;planning my feed-free week&lt;/a&gt;, I thought I needed an app/website blocker. But starting &lt;a href=&quot;/2026/02/16/NoFeeds1/&quot;&gt;the first day&lt;/a&gt; with an intention was enough. &lt;em&gt;“No social media today.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead of scrolling, I read.&lt;/p&gt;

&lt;p&gt;That week, I finished &lt;a href=&quot;/2026/02/22/Papyrus/&quot;&gt;Papyrus by Irene Vallejo&lt;/a&gt; and got halfway through Financial Freedom by Grant Sabatier. In fact, I’ve been &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;replacing my phone time&lt;/a&gt; with books in the last month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #1:&lt;/strong&gt; Don’t simply ban social media, replace them with conscious activities instead.&lt;/p&gt;

&lt;h2 id=&quot;dont-make-scrolling-your-go-to-activity&quot;&gt;Don’t make scrolling your go-to activity&lt;/h2&gt;

&lt;p&gt;I hadn’t realized how scrolling had changed my behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2026/02/17/NoFeeds2/&quot;&gt;On day two&lt;/a&gt;, I was on a weekly meeting with a contracting client. “Another meeting that could be an email” kind of meeting. Nothing unusual until I noticed I wanted to fire up my LinkedIn account to make it until the end. I wanted a feed to scroll, like an addict.&lt;/p&gt;

&lt;p&gt;Those moments made me realize scrolling was my default response to boredom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #2:&lt;/strong&gt; When procrastination comes, start with an easy task and sustain for 5 minutes. That’s enough to keep things moving forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #3:&lt;/strong&gt; Find a boring activity instead of scrolling on social media. I’m doodling on the back of old receipts.&lt;/p&gt;

&lt;h2 id=&quot;social-media-looks-like-any-other-addiction&quot;&gt;Social media looks like any other addiction&lt;/h2&gt;

&lt;p&gt;Even with an intention, books, and doodling, my monkey brain often popped up trying to trick me back to social media:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“What if my account got suspended for lack of engagement?”&lt;/li&gt;
  &lt;li&gt;“Did my posts generate visits to my book landing pages?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To quiet the monkey brain, I learned to distance from it and refute my thoughts with rational ones:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“You received a notification of your scheduled posts going live. The system is working.”&lt;/li&gt;
  &lt;li&gt;“Let your content system run. You don’t need to refresh stats every 5 minutes. Let the Internet do its magic.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson #4:&lt;/strong&gt; Social media tricks (whistles, notifications, bells) work. We need a lot of effort and intention to fight back.&lt;/p&gt;

&lt;h2 id=&quot;warming-up-the-algorithm-isnt-worth-the-time&quot;&gt;“Warming up” the algorithm isn’t worth the time&lt;/h2&gt;

&lt;p&gt;To finish the experiment, earlier today I logged into my social media accounts.&lt;/p&gt;

&lt;p&gt;In some platforms, my impressions suffered. Maybe it was just coincidence. In another one, one of the two scheduled long-form posts was a hit.&lt;/p&gt;

&lt;p&gt;As usual, my best posts still spiked in views and thoughtful comments.&lt;/p&gt;

&lt;p&gt;“Warming up” the algorithm with hacks (engaging before posting, like and reshare your own posts) isn’t worth the time and the addiction it creates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #5:&lt;/strong&gt; Good posts stand out without hacks.&lt;/p&gt;

&lt;h2 id=&quot;handling-them-with-care&quot;&gt;Handling them with care&lt;/h2&gt;

&lt;p&gt;After my feed-free week, here are some long-term changes I’m adopting, both as a reader and writer:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;No email notifications from social media.&lt;/li&gt;
  &lt;li&gt;Zero feeds before creative work. Timer on socials for conscious use.&lt;/li&gt;
  &lt;li&gt;Weekly posts scheduled in advance. Have 2 or 3 weekly slots for engagement.&lt;/li&gt;
  &lt;li&gt;Feed-free mode when working on creative projects or experiments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I thought I wasn’t an addict. I thought I used &lt;a href=&quot;/2026/01/19/LinkedIn/&quot;&gt;social media consciously&lt;/a&gt;. I thought I was a dealer who didn’t use their own drug. Stepping away showed me I was just another dopamine junkie. Social media, like every billion-dollar industry, thrives on keeping us hooked.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Key Hack That Will Save You a Lot of Headaches And Nightmares</title>
   <link href="https://canro91.github.io/2026/02/24/Keys/"/>
   <updated>2026-02-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/24/Keys</id>
   <content type="html">&lt;p&gt;Here’s the hack:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Make sure everybody at home has copies of all keys, like every door and lock.&lt;/li&gt;
  &lt;li&gt;Give a trusted friend spare keys and a backup with your password manager’s recovery key.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Not that I had to break into my own apartment…Wink, wink! It sounds funny until it happens.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Lessons From Papyrus on How Books Began</title>
   <link href="https://canro91.github.io/2026/02/22/Papyrus/"/>
   <updated>2026-02-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/22/Papyrus</id>
   <content type="html">&lt;p&gt;When you hear the word “book,” what comes to mind?&lt;/p&gt;

&lt;p&gt;A cover, back cover, and spine? Sheets of paper bound to flip from left to right? Printing machines?&lt;/p&gt;

&lt;p&gt;Books are recent, but their fascinating origins stretch back thousands and thousands of years. That’s precisely what Irene Vallejo tells us in &lt;em&gt;Papyrus: The Invention of Books in the Ancient World&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here are 7 lessons I took away from this book:&lt;/p&gt;

&lt;h2 id=&quot;1-kids-didnt-learn-with-easy-books&quot;&gt;#1. Kids didn’t learn with easy books.&lt;/h2&gt;

&lt;p&gt;My dad bought my sister a book with Franklin the turtle every time she won an exam. Like her, we grew up reading books for kids: easy sentences filled with images.&lt;/p&gt;

&lt;p&gt;But kids in Ancient Greece didn’t have easy books. They learned with the Iliad and Odyssey by Homer. To make things worse, ancient Greek didn’t have spaces, paragraphs, or punctuation marks…and teachers were ruthless.&lt;/p&gt;

&lt;h2 id=&quot;2-book-copyists-worked-for-centuries&quot;&gt;#2. Book copyists worked for centuries.&lt;/h2&gt;

&lt;p&gt;In Athens, getting a book meant sending someone to Alexandria, Egypt to copy it. Sometimes I complain when it takes a month to receive a book.&lt;/p&gt;

&lt;h2 id=&quot;3-titles-binding-and-pocket-books-are-recent-inventions&quot;&gt;#3. Titles, binding, and pocket books are recent inventions.&lt;/h2&gt;

&lt;p&gt;For centuries, we had rolls instead of books.&lt;/p&gt;

&lt;p&gt;To read a scroll, you rolled it with your left hand while unrolling it with your right one. When finished, you rolled it back up as a courtesy for the next reader. Instead of titles, librarians categorized books by their opening sentences.&lt;/p&gt;

&lt;h2 id=&quot;4-books-were-read-out-loud&quot;&gt;#4. Books were read out loud.&lt;/h2&gt;

&lt;p&gt;In school, I learned to read sitting in a circle taking turns to read a passage from an easy story. Then, we had to do it in silence.&lt;/p&gt;

&lt;p&gt;But in ancient times, reading out loud was the norm. Reading a book was like a small prayer, a soft monologue.&lt;/p&gt;

&lt;h2 id=&quot;5-iliad-and-odyssey-were-best-sellers&quot;&gt;#5. Iliad and Odyssey were best-sellers.&lt;/h2&gt;

&lt;p&gt;Probably every educated citizen in Ancient Times knew about them. Even kids learned to read with them.&lt;/p&gt;

&lt;h2 id=&quot;6-education-included-the-body-as-well-as-the-mind&quot;&gt;#6. Education included the body as well as the mind.&lt;/h2&gt;

&lt;p&gt;Apart from reading, men had to work on their bodies as part of their education. A privilege reserved for higher classes and free men. Slaves were meant only for manual labor.&lt;/p&gt;

&lt;p&gt;To strengthen their bodies, men trained outdoors or in &lt;em&gt;gymnasiums&lt;/em&gt; showing off their bodies…while naked. Next time you see some “bros” posing and taking selfies in front of a mirror after lifting some weights at the gym, remember it used to be worse in ancient times.&lt;/p&gt;

&lt;h2 id=&quot;7-books-wont-die&quot;&gt;#7. Books won’t die.&lt;/h2&gt;

&lt;p&gt;Books haven’t always looked the same: clay, stone, leather, papyrus, codices, paper… But they have endured the test of time. eBooks, summaries, TikTok, or AI aren’t a real threat for books. We’ve had them for centuries. And we will for more.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Simple Habits That Improved My Digestion</title>
   <link href="https://canro91.github.io/2026/02/22/Digestion/"/>
   <updated>2026-02-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/22/Digestion</id>
   <content type="html">&lt;p&gt;Burnout brought stomach issues I couldn’t ignore.&lt;/p&gt;

&lt;p&gt;In 2023, one day, I suddenly had to rush to the bathroom after eating. I wanted to finish my work so fast that I skipped meals and developed poor eating habits.&lt;/p&gt;

&lt;p&gt;After many doctor visits, here’s a compilation of &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;10 ideas&lt;/a&gt; for better digestion and eating habits.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Eat at consistent times.&lt;/li&gt;
  &lt;li&gt;Eat liquids and solids separately.&lt;/li&gt;
  &lt;li&gt;Chew every bite at least 10 times.&lt;/li&gt;
  &lt;li&gt;Remove distractions like phones and news while eating.&lt;/li&gt;
  &lt;li&gt;Follow the right order to avoid &lt;a href=&quot;/2026/01/21/GlucoseSpikes/&quot;&gt;glucose spikes&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Stop before you’re completely full.&lt;/li&gt;
  &lt;li&gt;Pause briefly after finishing.&lt;/li&gt;
  &lt;li&gt;Drink water with lemon or apple cider 30 minutes before meals.&lt;/li&gt;
  &lt;li&gt;Drink chamomile or spearmint tea after eating. Spearmint tea is my favorite.&lt;/li&gt;
  &lt;li&gt;Leave at least 12 hours between dinner and breakfast.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A word of caution: this is not medical advice, just what worked for me.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Big change, fake 7zip, and mourning our craft</title>
   <link href="https://canro91.github.io/2026/02/20/FridayLinks/"/>
   <updated>2026-02-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/20/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; &lt;a href=&quot;https://shumer.dev/something-big-is-happening&quot;&gt;Something big is already happening&lt;/a&gt;. AI may not be disrupting markets, like most CEOs claim. But it’s definitely changing job descriptions. Saving is one step to prepare.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Someday we will tell our grandkids that dad once wrote code by hand. We’re the last in a generation of coders. &lt;a href=&quot;https://nolanlawson.com/2026/02/07/we-mourn-our-craft/&quot;&gt;Now is the time to mourn our craft&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; &lt;a href=&quot;https://www.malwarebytes.com/blog/threat-intel/2026/02/fake-7-zip-downloads-are-turning-home-pcs-into-proxy-nodes&quot;&gt;A fake 7-Zip download turned PCs into zombie machines&lt;/a&gt; (10min). It all started with the wrong url. We’re still the weakest link.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Postman &lt;a href=&quot;https://codingismycraft.blog/index.php/2026/02/05/postman-from-api-client-to-everything-app/&quot;&gt;from beloved API client to an “everything” app&lt;/a&gt; (5min). It’s become “a completely feature-creeped product.”&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I’ve been writing on my blog about &lt;a href=&quot;/2026/02/15/NoFeeds/&quot;&gt;my social media free week&lt;/a&gt; (1min). Here are my &lt;a href=&quot;/2026/02/16/NoFeeds1/&quot;&gt;day 1&lt;/a&gt; (2min) and &lt;a href=&quot;/2026/02/17/NoFeeds2/&quot;&gt;day 2&lt;/a&gt; (1min) updates. In the meantime, I’ve picked two books and worked on another book project. (This won’t be about coding. More updates soon!)&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;em&gt;Street-Smart Coding,&lt;/em&gt; 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until next Friday. Keep coding smartly&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Day Four Without Social Media: Real Connection</title>
   <link href="https://canro91.github.io/2026/02/19/NoFeeds4/"/>
   <updated>2026-02-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/19/NoFeeds4</id>
   <content type="html">&lt;p&gt;On my fourth day &lt;a href=&quot;/2026/02/15/NoFeeds/&quot;&gt;without social media&lt;/a&gt;, I noticed how email has reshaped my morning.&lt;/p&gt;

&lt;p&gt;I know I should &lt;a href=&quot;/2025/07/16/ProductivityTips/&quot;&gt;protect my sacred hours&lt;/a&gt; by staying away from distractions first thing in the morning. Anyways…&lt;/p&gt;

&lt;p&gt;Checking my email for health insurance paperwork changed my mood. And soon I was thinking about my book pages stats and sales. They’re not a feed, but as addictive as social media. I lost track of time checking stats.&lt;/p&gt;

&lt;p&gt;Avoiding feeds was easier when I closed my laptop and went to a cafe to chat with my family. It wasn’t just coffee. And by choosing a local cafe, we supported our community over major chains.&lt;/p&gt;

&lt;p&gt;We embraced social media’s promise of connecting us via the internet. But we’ve forgotten to truly connect in real life.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to Remove a File From a Git Commit</title>
   <link href="https://canro91.github.io/2026/02/18/RemoveFile/"/>
   <updated>2026-02-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/18/RemoveFile</id>
   <content type="html">&lt;p&gt;I’ve hired AI as my code reviewer.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2026/01/26/AnotherAIRule/&quot;&gt;When I write code, AI reviews it&lt;/a&gt;. For that, I feed Copilot with a diff to review. But &lt;a href=&quot;/2025/10/22/Googling/&quot;&gt;I always have to Google&lt;/a&gt; how to diff two branches.&lt;/p&gt;

&lt;p&gt;To avoid Googling it every time, here it is:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git diff development..mybranch &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; diff
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Today, by accident, I committed the actual diff. So I had to remove it from a commit. I wasn’t sure if I needed a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase&lt;/code&gt; or something else. I had to Google it.&lt;/p&gt;

&lt;p&gt;Again, to avoid Googling it every time, &lt;a href=&quot;https://stackoverflow.com/a/15321456&quot;&gt;[Source]&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git reset &lt;span class=&quot;nt&quot;&gt;--soft&lt;/span&gt; HEAD~1  &lt;span class=&quot;c&quot;&gt;# Undo the last commit, keeping the changes&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Do your thing&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git commit &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; ORIG_HEAD  &lt;span class=&quot;c&quot;&gt;# Commit, using the last message&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Day Two Without Social Media (And Two Lessons On Financial Freedom)</title>
   <link href="https://canro91.github.io/2026/02/17/NoFeeds2/"/>
   <updated>2026-02-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/17/NoFeeds2</id>
   <content type="html">&lt;p&gt;Dear diary:&lt;/p&gt;

&lt;p&gt;Today was my second day without social media. I’m starting to notice the changes. I took notes as the day went by and tracked my feelings.&lt;/p&gt;

&lt;p&gt;I started reading another chapter of &lt;em&gt;Financial Freedom&lt;/em&gt; by Grant Sabatier. Main lessons?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Financial freedom is about time.&lt;/strong&gt; Having more time for you. Using time to make your money work earlier and for longer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Money is infinite.&lt;/strong&gt; We can’t make more time, but we can always make more money.&lt;/p&gt;

&lt;p&gt;After my reading time, I joined a meeting with my contracting client.&lt;/p&gt;

&lt;p&gt;I was so tempted to check my email or fire up LinkedIn. That’s usually what I do during long, unproductive meetings. That’s how my meeting time looked. For this one, my only contribution was, &lt;em&gt;“Yes, that’s correct!”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead of scrolling, I took a receipt and doodled circles. I drew a big circle in the middle, then smaller ones, then smaller ones…until the page was full. That’s a drawing exercise I found the other day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Big observation:&lt;/strong&gt; When I feel like procrastinating, I used to scroll. Now I count to three, finish a tiny task, and sustain for five minutes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Day One Without Social Media: What I Learned</title>
   <link href="https://canro91.github.io/2026/02/16/NoFeeds1/"/>
   <updated>2026-02-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/16/NoFeeds1</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Yesterday I began an week-long experiment: &lt;a href=&quot;/2026/02/15/NoFeeds/&quot;&gt;no social media, no feeds&lt;/a&gt;. I’m writing this at the end of the first day of the experiment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’s what I noticed after one day without social media:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Start with an intention.&lt;/strong&gt; Usually before working, I check my email and log into Medium and other accounts to reply to comments. Today I didn’t need a website blocker since I started with an intention: &lt;em&gt;no social media.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Read more.&lt;/strong&gt; I filled the slot before working with a book. I picked &lt;em&gt;Financial Freedom&lt;/em&gt; by Grant Sabatier. Then after lunch, instead of scrolling, I kept reading another book. Yes, I read more than one book at once—One strategy to &lt;a href=&quot;/2025/04/28/OneBookAWeek/&quot;&gt;read more books&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Time for side projects.&lt;/strong&gt; With the extra time, I worked on &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;my February book project&lt;/a&gt;. &lt;a href=&quot;/2026/02/09/FirstDraft/&quot;&gt;The first draft is ready&lt;/a&gt;. And after a day’s break, I’m now editing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. An alarm as reminder.&lt;/strong&gt; The other day, I found out about &lt;a href=&quot;/2024/12/18/3Alarms/&quot;&gt;the 3-alarm method&lt;/a&gt;. I’m using it to remind me of a “Do Nothing” time to &lt;a href=&quot;/2026/02/03/Boredom/&quot;&gt;embrace boredom&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Taking a Week Off Social Media And Feeds</title>
   <link href="https://canro91.github.io/2026/02/15/NoFeeds/"/>
   <updated>2026-02-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/15/NoFeeds</id>
   <content type="html">&lt;p&gt;A post on social media convinced me to quit it for a week. Ironic, right?&lt;/p&gt;

&lt;p&gt;The other day, a LinkedIn connection shared that &lt;em&gt;“we don’t owe the Internet consistency.”&lt;/em&gt; It was an invitation to disappear from social media without apologizing.&lt;/p&gt;

&lt;p&gt;That’s what I’m doing this week.&lt;/p&gt;

&lt;p&gt;To &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;cut phone time&lt;/a&gt; and &lt;a href=&quot;/2026/02/03/Boredom/&quot;&gt;embrace boredom&lt;/a&gt;, I’m quitting feeds for a week. That’s Medium, LinkedIn, dev.to, YouTube…&lt;/p&gt;

&lt;p&gt;Quitting feeds frees up between 45-60 minutes each day. Yes, that’s how long I scroll down feeds on average, even when &lt;a href=&quot;/2026/01/19/LinkedIn/&quot;&gt;using social media consciously&lt;/a&gt;. Every time I open a social media app, I remind  myself: &lt;em&gt;“creators don’t consume.”&lt;/em&gt; A reminder to &lt;a href=&quot;/2026/02/14/SomethingYouLike/&quot;&gt;spend time making&lt;/a&gt;, not scrolling.&lt;/p&gt;

&lt;p&gt;Even though I’m disappearing, I’ve scheduled posts so my system runs without me.&lt;/p&gt;

&lt;p&gt;I’m planning to use that extra time to &lt;a href=&quot;/2025/09/08/StoicBookReading/&quot;&gt;read books&lt;/a&gt; and finish &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;my February book experiment&lt;/a&gt;. I will share the results soon.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Make Something You Like—At Least One Person Will Like It</title>
   <link href="https://canro91.github.io/2026/02/14/SomethingYouLike/"/>
   <updated>2026-02-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/14/SomethingYouLike</id>
   <content type="html">&lt;p&gt;At times, writing (or creating anything) can feel pointless.&lt;/p&gt;

&lt;p&gt;We don’t see results or traction. That’s when we should think &lt;a href=&quot;/2025/10/08/PastSelf/&quot;&gt;we’re writing for our past selves&lt;/a&gt;, documenting our journey, or &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;creating a time capsule&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But these days, my scrolling led me to &lt;em&gt;&lt;a href=&quot;https://essays.fnnch.com/make-a-living&quot;&gt;How to Make a Living as an Artist&lt;/a&gt;&lt;/em&gt;. A street artist wrote it for other artists. But the next line captures the essence of writing or any other creative pursuit:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;If you make something that you like, at least one person will like it — you. If you make something you think other people will like, you run the risk of no one liking it at all. That would be sad.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Write what you’d read. Code the app you’d use. Paint what you’d hang in your living room. At least one person will like it. That’s enough.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: 1-pizza teams, being useful, and losing mastery</title>
   <link href="https://canro91.github.io/2026/02/13/FridayLinks/"/>
   <updated>2026-02-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/13/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; When coding was the bottleneck, we used 2-pizza teams. Now, with AI writing decent code, we have to rethink team organization. AI is turning teams into &lt;a href=&quot;https://www.jampa.dev/p/the-rise-of-one-pizza-engineering&quot;&gt;1-pizza teams&lt;/a&gt; (7min) while creating new roles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Most of us coders are problem solvers at heart. The worst part is that makes us &lt;a href=&quot;https://www.seangoedecke.com/addicted-to-being-useful/&quot;&gt;addicted to being useful&lt;/a&gt; (6min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; We are at &lt;a href=&quot;https://brooker.co.za/blog/2026/02/07/you-are-here.html&quot;&gt;a turning point of our career as coders&lt;/a&gt; (5min). Software’s first “season” is over. But the next one will be &lt;em&gt;“more interesting, more economically valuable, and more mentally stimulating than we can imagine right now.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Anthropic found that over-relying on AI leads to a “statistically significant decrease in mastery.” &lt;a href=&quot;https://blog.jim-nielsen.com/2026/study-finds-obvious-truth/&quot;&gt;A truth everybody already knows&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/02/06/AIStats/&quot;&gt;the stats that deflate the AI hype&lt;/a&gt; (2min) (more revealing that Anthropic’s study) and &lt;a href=&quot;/2026/02/03/Boredom/&quot;&gt;how I’m embracing boredom&lt;/a&gt; (3min) (My phone screen time was quite shocking).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;em&gt;Street-Smart Coding,&lt;/em&gt; 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until next Friday. Keep coding smartly&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Build Real Coding Skills—Then Use AI (In That Order)</title>
   <link href="https://canro91.github.io/2026/02/12/RealSkills/"/>
   <updated>2026-02-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/12/RealSkills</id>
   <content type="html">&lt;p&gt;In the days of StackOverflow, we had to verify answers. Now, too often, we accept AI’s output without question.&lt;/p&gt;

&lt;h2 id=&quot;catching-ai-red-handed&quot;&gt;Catching AI red-handed&lt;/h2&gt;

&lt;p&gt;Today, in another adventure with AI, &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;I asked Copilot&lt;/a&gt; to turn a couple of SQL table definitions into mapping classes for Entity Framework Core. It was the classical 1-to-many relationship.&lt;/p&gt;

&lt;p&gt;The problem came when I asked it to generate an API endpoint to store a parent record with a bunch of child records. Something like: create a parent record, then read a table to create its children.&lt;/p&gt;

&lt;p&gt;Its first solution was to persist the parent record. Then inside a loop, persist every child record. The classical N+1 problem. Well, the inverse one. Arrggg!&lt;/p&gt;

&lt;p&gt;When I prompted it to change it, saying there was no need for the loop, it replied with a &lt;em&gt;“Yes, you can simplify it that way.”&lt;/em&gt; Caught you Copilot!&lt;/p&gt;

&lt;h2 id=&quot;why-coding-skills-still-matter&quot;&gt;Why coding skills still matter&lt;/h2&gt;

&lt;p&gt;The N+1 problem was something I could find on the spot.&lt;/p&gt;

&lt;p&gt;Now imagine how many AI answers we blindly accept without question. When coding, documenting, researching, testing…&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Coding skills still matter. Without them, we wouldn’t even notice the problem.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Blindly trusting AI is what makes us say &lt;a href=&quot;/2025/12/22/AIRuiningDegrees/&quot;&gt;AI kills CS degrees&lt;/a&gt;, what &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;makes us dangerously lazy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Reviewing the code AI spits out puts in &lt;a href=&quot;/2026/02/06/AIStats/&quot;&gt;the top 50% of coders&lt;/a&gt;. The other 50% don’t always review. You need your coding muscles for that.&lt;/p&gt;

&lt;p&gt;AI is like a semi-autonomous car. It always needs hands on the wheel. Build skills. Then leverage AI.&lt;/p&gt;

&lt;p&gt;To help you build hype-proof skills, I wrote &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=build-real-coding-skills-use-ai&quot;&gt;Street-Smart Coding&lt;/a&gt;. Because syntax alone won’t make you stand out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Interesting (But Random) Ideas I Stumbled Upon Recently</title>
   <link href="https://canro91.github.io/2026/02/11/RandomThoughts/"/>
   <updated>2026-02-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/11/RandomThoughts</id>
   <content type="html">&lt;p&gt;Even on &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;a phone reduction diet&lt;/a&gt;, and even &lt;a href=&quot;/2026/01/19/LinkedIn/&quot;&gt;using social media consciously&lt;/a&gt;, I found myself wondering what to write next. I had too much on my mind.&lt;/p&gt;

&lt;p&gt;To offload my mind, here’s a single-take brain dump of random ideas I’ve found recently:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Nothing can’t be done by thinking alone.&lt;/strong&gt; That’s a line I found in &lt;em&gt;Secrets to Thrive in Life&lt;/em&gt;, a book &lt;a href=&quot;/2026/01/20/WritingInBooks/&quot;&gt;my mom annotated&lt;/a&gt; and left behind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Don’t network. Write instead.&lt;/strong&gt; Your words have wider reach than a networking activity or event. Someone shared in Medium.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. A fit, shredded body is a status symbol.&lt;/strong&gt; A fancy house, car, and watch can be financed, but not a healthy body. Allegedly Arnold Schwarzenegger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. The bigger the project, the strongest the Resistance.&lt;/strong&gt; Resistance being the personification of doubt, procrastination, and self-sabotage. From the one and only, Steven Pressfield.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. When stressed/anxious/overwhelm, create.&lt;/strong&gt; Someone else in Medium.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. You don’t owe the Internet consistency.&lt;/strong&gt; It’s OK to disappear from social media. A creator I follow in LinkedIn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Journaling can be as simple as writing one line a day.&lt;/strong&gt; Ryan Holiday from his YouTube channel.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unpopular Opinions on AI-assisted Coding That May Annoy You</title>
   <link href="https://canro91.github.io/2026/02/10/UnpopularOpinions/"/>
   <updated>2026-02-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/10/UnpopularOpinions</id>
   <content type="html">&lt;p&gt;Vibecoding was bad. But now, AI-assisted coding seems fine.&lt;/p&gt;

&lt;p&gt;Nothing sparks more heated discussions than asking coders about best practices. Today, someone I follow on LinkedIn shared his weekend AI experiment to build an app. As usual, “passionate” coders threw virtual stones.&lt;/p&gt;

&lt;p&gt;To turn the conversation around, he asked for our unpopular opinions about AI-assisted coding. To avoid burying mine in a comment, here they are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Use AI as a calculator.&lt;/strong&gt; Only useful if you know what you’re doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Don’t let AI touch code directly.&lt;/strong&gt; That’s &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;my go-to rule for coding with AI&lt;/a&gt;. Unproductive? Maybe. But it forces me to decompose problems and validates AI-generated code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Use AI for opposite tasks.&lt;/strong&gt; &lt;a href=&quot;/2025/10/20/SloppyAI/&quot;&gt;This is my most recent rule&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If I write code, AI reviews it. If AI generates it, I review it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;According to &lt;a href=&quot;/2026/02/06/AIStats/&quot;&gt;a recent Sonar survey&lt;/a&gt;, only 48% of respondents &lt;em&gt;always&lt;/em&gt; check AI-assisted code before committing. &lt;em&gt;#yolo&lt;/em&gt; By reviewing, I’m already in the top 50%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. AI is like a semi-autonomous car.&lt;/strong&gt; You trust it to steer, but you never take your hand off the wheel. Otherwise, &lt;a href=&quot;/2025/10/20/SloppyAI/&quot;&gt;AI could be a sloppy coder with bad memory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AI alone won’t make you a great coder. It only amplifies the skills you already have. That’s why I wrote &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=unpopular-opinions-aiassisted-coding-that-may-annoy-you&quot;&gt;Street-Smart Coding&lt;/a&gt;—because you need more than syntax to stand out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What to Do After Finishing Your First Draft: An Update on My Book Experiment</title>
   <link href="https://canro91.github.io/2026/02/09/FirstDraft/"/>
   <updated>2026-02-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/09/FirstDraft</id>
   <content type="html">&lt;p&gt;After a week of typing, my next book’s first draft is done.&lt;/p&gt;

&lt;p&gt;This month, I’m running &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;a book experiment&lt;/a&gt;: I’m turning a hit post into a short guide, but backwards.&lt;/p&gt;

&lt;p&gt;Today I transcribed the last chapters. Yes, &lt;a href=&quot;/2026/02/07/Handwriting/&quot;&gt;I handwrote some of the chapters&lt;/a&gt;. The draft is 7,084 words across 24 pages in Google Docs. That’s the minimum page count for an Amazon KDP paperback. Formatting and front/back matter will add more pages.&lt;/p&gt;

&lt;p&gt;Even when hitting a small victory, like finishing the first draft, my inner voice speaks louder. &lt;em&gt;“Do I have something good?”&lt;/em&gt; &lt;em&gt;“Are people going to like it?”&lt;/em&gt; I have to trust the process and focus on the next task.&lt;/p&gt;

&lt;p&gt;Now that the first draft is done, here’s what I’m doing:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Take distance. I’m letting the draft sit for a few days to read it with fresh eyes.&lt;/li&gt;
  &lt;li&gt;Replace placeholders. To finish my first draft in a single pass, I used “XXX” for places where I needed to fill in details later.&lt;/li&gt;
  &lt;li&gt;Find typos. I use &lt;a href=&quot;/2025/03/01/ReplacingGrammarly/&quot;&gt;a prompt to proofread my writing&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Reduce duplication. If I repeat anecdotes or stories, I’ll vary them or reference earlier mentions.&lt;/li&gt;
  &lt;li&gt;Revisit the opening and closing paragraphs of every chapter.&lt;/li&gt;
  &lt;li&gt;Revisit the first and last chapters. They leave the first and last impression.&lt;/li&gt;
  &lt;li&gt;Print it and read it with pen in hand.&lt;/li&gt;
  &lt;li&gt;Ask someone else to read it.&lt;/li&gt;
  &lt;li&gt;Give it another break before revisiting.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update (Mar 2026):&lt;/strong&gt;&lt;/em&gt; &lt;a href=&quot;/2026/03/07/BookExperiment/&quot;&gt;This experiment was a success&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What I&apos;m Working On–February 2026</title>
   <link href="https://canro91.github.io/2026/02/08/Now/"/>
   <updated>2026-02-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/08/Now</id>
   <content type="html">&lt;p&gt;Inspired by &lt;a href=&quot;https://news.ycombinator.com/item?id=46937696&quot;&gt;a Hacker News thread&lt;/a&gt;, here’s what I’m working on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. My health:&lt;/strong&gt; After falling ill in 2023-2024, I began caring daily for my body, mind, and spirit daily, and more recently, for my eating habits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Less phone time:&lt;/strong&gt; With about 2 hours of average screen time per day, I’m working to &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;reduce my phone time&lt;/a&gt; and &lt;a href=&quot;/2026/02/03/Boredom/&quot;&gt;embrace boredom&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. A book trilogy:&lt;/strong&gt; Before retiring from coding, I’m leaving all my lessons in books. My plan is to write a trilogy. I already wrote &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;. That’s a roadmap of coding skills. The first I wrote, but the second in the trilogy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. A book experiment:&lt;/strong&gt; This month, I’m running &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;a book experiment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m turning my most popular post about personal development into a short, actionable guide. My goal is to finish it in one month. I’ve made good progress on the first draft, even &lt;a href=&quot;/2026/02/07/Handwriting/&quot;&gt;handwriting some chapters&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Daily writing:&lt;/strong&gt; Last year, I hit &lt;a href=&quot;/2025/12/06/400DailyPosts/&quot;&gt;the 400th daily post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I began on &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;November 1, 2024&lt;/a&gt;, during a forced sabbatical after a layoff.&lt;/p&gt;

&lt;p&gt;I’ve considered abandoning daily writing or limiting it to workdays. But after a ban from a social blog, I’m doubling down on my blog and email list.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why I&apos;m Writing Some of My Posts By Hand (Plus An Update On My Book Experiment)</title>
   <link href="https://canro91.github.io/2026/02/07/Handwriting/"/>
   <updated>2026-02-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/07/Handwriting</id>
   <content type="html">&lt;p&gt;I’ve started writing some of my posts by hand.&lt;/p&gt;

&lt;p&gt;Last month, I started &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;reducing my phone time&lt;/a&gt;. To &lt;a href=&quot;/2026/02/03/Boredom/&quot;&gt;embrace boredom&lt;/a&gt;, I schedule a “Nothing” slot away from tech.&lt;/p&gt;

&lt;p&gt;The other day, it was “Nothing” time and I hadn’t written my daily post. So I decided to grab pen and paper to write it. Yes, I had to “work” twice: write and then type. But somehow it felt different.&lt;/p&gt;

&lt;h2 id=&quot;im-not-alone-in-handwriting&quot;&gt;I’m not alone in handwriting&lt;/h2&gt;

&lt;p&gt;This experiment made me think of Roberto Gomez Bolaños, aka Chespirito, who also relied on pen and paper to write scripts for his TV shows and sketches.&lt;/p&gt;

&lt;p&gt;When he started his writing career, he didn’t know how to use a typewriter. And later when he injured one of his hands, he kept the habit. At least, that’s what &lt;a href=&quot;/2025/07/28/Chespirito/&quot;&gt;his biopic&lt;/a&gt; shows.&lt;/p&gt;

&lt;h2 id=&quot;its-extra-work-but-heres-what-im-doing-less&quot;&gt;It’s extra work, but here’s what I’m doing less&lt;/h2&gt;

&lt;p&gt;For &lt;a href=&quot;/2026/02/01/BookExperiment/&quot;&gt;my February book experiment&lt;/a&gt;, I wrote the last two chapters by hand.&lt;/p&gt;

&lt;p&gt;Like Chespirito, I wrote by hand, but only a few chapters. I planned 13 chapters. I wrote the first draft of 12. 2 of them need transcribing. Chespirito probably had an assistant for transcriptions. I don’t.&lt;/p&gt;

&lt;p&gt;It might seem like extra work.&lt;/p&gt;

&lt;p&gt;But pen and paper force me to think hard before putting the pen down. I don’t have a backspace key. Well, I can cross out lines and scratch. But I don’t find myself going back to edit while I’m writing by hand.&lt;/p&gt;

&lt;p&gt;When I’m transcribing (like now. I handwrote this post first), I edit less. Handwriting and transcribing force me to switch tasks and detach from the original text—and of course, slow down from the rush of digital writing. That’s a point for pen and paper.&lt;/p&gt;

&lt;p&gt;The backspace key is a blessing, but sometimes a curse. And slowing down can be its own blessing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Moltbook, Notepad++ compromised, and code sculptors</title>
   <link href="https://canro91.github.io/2026/02/06/FridayLinks/"/>
   <updated>2026-02-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/06/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there. It’s been a wild week in tech.&lt;/p&gt;

&lt;p&gt;Here’s what stood up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; This is crazy. The &lt;a href=&quot;https://notepad-plus-plus.org/news/hijacked-incident-info-update/&quot;&gt;Notepad++ update server was compromised&lt;/a&gt; (7min) by Chinese state hackers for part of 2025. Patch yours right away!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Now there’s a Reddit for AI: Moltbook…or whatever the latest name is. Here’s a breakdown of why it’s &lt;a href=&quot;https://simonwillison.net/2026/Jan/30/moltbook/&quot;&gt;the most interesting place on the internet&lt;/a&gt; (7min). Imagine AIs ranting about their tasks. It seems they aren’t conspiring against us yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; AI isn’t replacing us, just changing our job description. Maybe we won’t be coders anymore. &lt;a href=&quot;https://www.jerpint.io/blog/2026-01-24-i-dont-write-code-anymore-i-sculpt-it/&quot;&gt;We will be code sculptors&lt;/a&gt; (2min). Imagine job descriptions: &lt;em&gt;“Looking for a 10x Michelangelo to join our amazing family.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; The only social media I use is LinkedIn. You probably found me there first. I avoid the feed though. If you want to jump in, here’s &lt;a href=&quot;https://danielleheberling.xyz/blog/mindful-social-media/&quot;&gt;a guide on how to use social media mindfully&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Thanks to the magic of the internet, a post from December got some traction last week. In case you missed it, here it is: &lt;a href=&quot;/2025/12/21/Offerings/&quot;&gt;what we could offer if AI finally takes over&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;em&gt;Street-Smart Coding,&lt;/em&gt; 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until next Friday. Keep coding smartly&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Numbers That Deflate the AI Hype About Replacing Coders—and Ease FOMO</title>
   <link href="https://canro91.github.io/2026/02/06/AIStats/"/>
   <updated>2026-02-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/06/AIStats</id>
   <content type="html">&lt;p&gt;Only 25% of developers regularly use &lt;em&gt;AI agents&lt;/em&gt;, according to Sonar’s survey.&lt;/p&gt;

&lt;p&gt;After &lt;a href=&quot;https://www.sonarsource.com/state-of-code-developer-survey-report.pdf&quot;&gt;surveying more than 1,100 developers&lt;/a&gt; across the globe, they found:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;90% of respondents use AI for assisting development. But only 55% of them rate AI as “extremely or very effective.”&lt;/li&gt;
  &lt;li&gt;96% don’t fully trust that AI-generated code is functionally correct.&lt;/li&gt;
  &lt;li&gt;48% always check their AI-assisted code before committing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; We’re flooded with headlines predicting the end of coders.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/12/08/CEOVsJanitor/&quot;&gt;AI generating more than X% of code at a FAANG&lt;/a&gt;. One CEO suggesting nobody else should learn to code. To then retract himself saying that replacing senior coders with AI is crazy. And companies using AI innovation as an excuse for more layoffs.&lt;/p&gt;

&lt;p&gt;Those numbers show CEOs spread panic to bump stock prices and fuel the euphoria. &lt;a href=&quot;/2025/10/15/AIRealReason/&quot;&gt;The real motivator isn’t productivity, but financial interest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; If you think you’re &lt;a href=&quot;/2025/12/28/AIFomo/&quot;&gt;missing the whole AI movement&lt;/a&gt;, let the dust settle down. Double down on the fundamentals, not &lt;a href=&quot;/2025/01/30/ChasingShinyObjects/&quot;&gt;shiny objects&lt;/a&gt;. Maybe it’s time to pick &lt;em&gt;Structure and Interpretation of Computer Programs&lt;/em&gt; or any classical textbook.&lt;/p&gt;

&lt;p&gt;With 96% not trusting AI, code reviewing, clean code, and security remain essential. Don’t throw away your copy of Clean Code. And even &lt;a href=&quot;/2025/12/21/Offerings/&quot;&gt;if AI takes over&lt;/a&gt;, there’s work for human coders to do.&lt;/p&gt;

&lt;p&gt;It’s easy to fall into the AI hype. AI wins on speed. But humans win on communication, collaboration, and problem-solving skills.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=numbers-that-deflate-ai-hype-replacing-coders&quot;&gt;Street-Smart Coding&lt;/a&gt; covers some of those skills. Follow this roadmap to build hype-proof skills and become the kind of coder AI can’t replace.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Blazor Breaks When Others Touch the DOM</title>
   <link href="https://canro91.github.io/2026/02/04/BlazorAndDOM/"/>
   <updated>2026-02-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/04/BlazorAndDOM</id>
   <content type="html">&lt;p&gt;In another episode of &lt;em&gt;Adventures with Blazor&lt;/em&gt;…&lt;/p&gt;

&lt;p&gt;Once more, I ran into the mysterious &lt;em&gt;“TypeError: Cannot read properties of null (reading ‘removeChild’)”&lt;/em&gt;. But unlike &lt;a href=&quot;/2025/12/09/Debugging/&quot;&gt;the last time&lt;/a&gt;, it wasn’t a problem in a third-party component that we had to replace. It was my fault.&lt;/p&gt;

&lt;p&gt;Wouldn’t it be great if the error message mentioned which property was null: an id, xpath, or CSS class?&lt;/p&gt;

&lt;p&gt;This time, a modal created a record and showed a success alert. But after dismissing it and closing the modal, the page broke with the same error.&lt;/p&gt;

&lt;p&gt;Here’s a Blazor page that recreates the scenario. A simple modal with a form that shows an alert bound to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_directorSavedSuccessfully&lt;/code&gt;. Notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt; wrapped around the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@if&lt;/code&gt; inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;BodyContent&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@page &quot;/directors&quot;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn btn-primary&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AddDirectorClicked&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Add Director&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;ModalDialog&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Add Director&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_editDialog&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;BodyContent&amp;gt;&lt;/span&gt;
        @if (_directorSavedSuccessfully)
        {
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alert alert-success alert-dismissible fade show&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alert&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                Director saved successfully.
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;button&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn-close&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-bs-dismiss=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alert&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Close&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                @*                                      ^^^^^ *@
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        }

        &lt;span class=&quot;nt&quot;&gt;&amp;lt;EditForm&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Model=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_editDirector&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;OnValidSubmit=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SaveDirectorClicked&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;directorForm&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;InputText&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bind-Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_editDirector.Name&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;form-control&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/EditForm&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/BodyContent&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;FooterContent&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;button&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn btn-secondary&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CloseClicked&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Close&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;form=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;directorForm&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn btn-primary&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Save&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/FooterContent&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ModalDialog&amp;gt;&lt;/span&gt;

@code {
    private ModalDialog? _editDialog;
    private bool _directorSavedSuccessfully;
    private Director _director;

    private void AddDirectorClicked()
    {
        _directorSavedSuccessfully = false;
        _editDialog?.Open();
    }

    private Task SaveDirectorClicked()
    {
        // Imagine we&apos;re calling an API here to save...
        _directorSavedSuccessfully = true;

        return Task.CompletedTask;
    }

    private void CloseClicked()
    {
        _directorSavedSuccessfully = false;
        _editDialog?.Close();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Bootstrap removed the alert node directly, via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-bs-dismiss=&quot;alert&quot;&lt;/code&gt;. But Blazor was still “tracking” it. So when the modal was closed, Blazor didn’t find the alert. Somebody else ate its cheese. And boom!&lt;/p&gt;

&lt;p&gt;The solution? Don’t let anything else touch the DOM, but Blazor. Wire that alert to a variable controlled by Blazor.&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-&amp;lt;button type=&quot;button&quot; class=&quot;btn-close&quot; data-bs-dismiss=&quot;alert&quot; aria-label=&quot;Close&quot;&amp;gt;&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+&amp;lt;button type=&quot;button&quot; class=&quot;btn-close&quot; @onclick=&quot;_ =&amp;gt; _directorSavedSuccessfully = false&quot; aria-label=&quot;Close&quot;&amp;gt;&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà! Let Blazor own the DOM.&lt;/p&gt;

&lt;p&gt;For debugging and problem-solving tips, read &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=blazor-breaks-others-touch-dom&quot;&gt;Street-Smart Coding&lt;/a&gt;. And it’s even more relevant now in the era of AI-assisted coding.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My Phone Showed Me I was the Enemy of Boredom (4 Ways to Embrace It)</title>
   <link href="https://canro91.github.io/2026/02/03/Boredom/"/>
   <updated>2026-02-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/03/Boredom</id>
   <content type="html">&lt;p&gt;I can’t remember the last time I stared at the ceiling, wondering &lt;em&gt;“what should I do now?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Growing up without smartphones or tablets made me a friend of boredom. I lived on a main street of a small city. When bored, I counted passing cars in my front yard. Sometimes, I competed with my uncle and sister. The one who counted more cars of a certain color won. Yellow taxis weren’t allowed.&lt;/p&gt;

&lt;p&gt;Years later, with an iPhone, work, and side projects, boredom turned into an enemy. I’ve had to work to bring it back.&lt;/p&gt;

&lt;h2 id=&quot;what-im-doing-to-be-bored-again&quot;&gt;What I’m doing to be bored again&lt;/h2&gt;

&lt;p&gt;The other day, I had so much on my mind that I had to &lt;a href=&quot;/2026/01/17/DoNothing/&quot;&gt;sit and do nothing&lt;/a&gt;. That was my wake-up call.&lt;/p&gt;

&lt;p&gt;To welcome boredom again:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Less phone time.&lt;/strong&gt; &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;I’m reducing my phone time&lt;/a&gt;. Now my phone is in another room. There are books where I used to put it. And I’m writing this with pen and paper first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. A “do nothing” slot.&lt;/strong&gt; Apologies for the irony, but I’ve set an alarm in my phone labeled, “Mandalas afternoon.” I used to color mandalas during my lowest emotional season. I need to change that label. It should be “do nothing afternoon” and honor it. Not that coloring mandalas is a bad idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Walking outdoors.&lt;/strong&gt; For my physical and mental health, I go running next to the ocean. I’m planning to buy an analog watch (a classical Casio. Anyone else?) and leave my phone at home or put it inside my bag in silent mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. An Amish hour.&lt;/strong&gt; I’ve moved my book reading time to one or two hours before bed, away from screens. One evening, after &lt;a href=&quot;/2026/01/27/ExpertAtFailing/&quot;&gt;a coffee with a friend&lt;/a&gt;, too much caffeine and late-night writing kept me awake for hours. So no more screens before bed. The next challenge: an Amish afternoon. Or even an Amish day. Why not?! I’ll just need to write my daily post in advance.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Forget the Hit Book. Write a Series</title>
   <link href="https://canro91.github.io/2026/02/02/SeriesNotHit/"/>
   <updated>2026-02-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/02/SeriesNotHit</id>
   <content type="html">&lt;p&gt;&lt;em&gt;“Is there a next part? Are you covering the next stage after being a good coder?”&lt;/em&gt; a friend texted me.&lt;/p&gt;

&lt;p&gt;He finished &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;, my first book, and was eager for more. A reminder of a marketing lesson I had forgotten.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The best way to promote your book is by writing another one.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I first heard &lt;a href=&quot;/2025/09/30/PromotingABook/&quot;&gt;this surprising strategy&lt;/a&gt; on James Altucher’s podcast.&lt;/p&gt;

&lt;p&gt;Nicolas Cole, author of &lt;em&gt;The Art and Business of Online Writing&lt;/em&gt;, teaches the same strategy.&lt;/p&gt;

&lt;p&gt;On his YouTube channel, he advises building a portfolio of books instead of chasing one big launch. When readers finish and enjoy your book, they want more. One single title after a big launch doesn’t meet that need.&lt;/p&gt;

&lt;p&gt;That’s what happened to my friend after reading my book. And that’s more encouragement to work on the next one.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Running a Book Experiment This February</title>
   <link href="https://canro91.github.io/2026/02/01/BookExperiment/"/>
   <updated>2026-02-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/02/01/BookExperiment</id>
   <content type="html">&lt;p&gt;I’m writing a book backwards.&lt;/p&gt;

&lt;p&gt;Technically, it’s &lt;a href=&quot;/2025/12/27/MiniBookModel/&quot;&gt;a mini-book&lt;/a&gt;. I’m turning a hit post into a book.&lt;/p&gt;

&lt;p&gt;In the spirit of &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;doing 10,000 experiments&lt;/a&gt; and to &lt;a href=&quot;/2025/09/16/ContentExperiments/&quot;&gt;keep running content experiments&lt;/a&gt;, here’s what I’m doing this month:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Repurpose a hit post.&lt;/strong&gt; I’m turning one of my most read and liked posts into a short book. Over 100 people liked that post. Proof that the idea works. I’m even naming the book after the post.&lt;/p&gt;

&lt;p&gt;That’s what the authors of &lt;em&gt;The Subtle Art of Not Giving a Fuck&lt;/em&gt; and &lt;em&gt;The Psychology of Money&lt;/em&gt; did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Find inspiration from comments.&lt;/strong&gt; My hit post got a decent amount of discussion. I’m using those comments to find keywords, taglines, and objections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Expand it.&lt;/strong&gt; The source post is a 7-point listicle. I’m turning it into a 10-chapter concise book, like &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt;. Each point expands into one or two pages with stories and past posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Write it backwards.&lt;/strong&gt; Instead of jumping to the introduction, I’m:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Choosing a title&lt;/li&gt;
  &lt;li&gt;Writing &lt;a href=&quot;/2025/11/09/OneLine/&quot;&gt;a one-line summary&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Outlining the content&lt;/li&gt;
  &lt;li&gt;Finding Amazon keywords and categories&lt;/li&gt;
  &lt;li&gt;Sketching a sales page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m even stealing cover ideas before writing a word.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Make it short.&lt;/strong&gt; The other day, I found &lt;a href=&quot;/2026/01/14/TheShortestBook/&quot;&gt;a one-page book&lt;/a&gt;, so why not write 10 or 15 pages and call it a book? One or two pages per point plus the front and back matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Price it incrementally.&lt;/strong&gt; I’m following the “$0.99 is the new free” idea. If it gains traction, I’ll raise the price by $1 every other month until it hits $5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Hit one reader milestone.&lt;/strong&gt; Just like I set it for &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;, if even one reader beyond my circle buys it, the experiment is a success.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update (Mar 2026):&lt;/strong&gt;&lt;/em&gt; &lt;a href=&quot;/2026/03/07/BookExperiment/&quot;&gt;This experiment was a success&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Better A Small Rep Than A Broken Habit</title>
   <link href="https://canro91.github.io/2026/01/31/BetterSmallThanNone/"/>
   <updated>2026-01-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/31/BetterSmallThanNone</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Yesterday I found a LinkedIn post asking if a crap post was better than no post at all. I didn’t want to bury my answer on the comments section. So I’m expanding it a bit here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A crap post is better than silence.&lt;/p&gt;

&lt;p&gt;Well, &lt;a href=&quot;/2025/01/02/SturgeonLaw/&quot;&gt;90% of everything is crap&lt;/a&gt;. Most of what we create won’t be great. And that’s ok.&lt;/p&gt;

&lt;p&gt;But writing, coding, or any creative pursuit is like exercising. Skip the gym once, nothing happens. Skip twice, and suddenly you’re on the couch, binge-watching Netflix, wondering where the extra weight came from.&lt;/p&gt;

&lt;p&gt;Whether it’s &lt;a href=&quot;/2024/11/13/BlogMore/&quot;&gt;a post slightly longer than a Tweet&lt;/a&gt;, some &lt;a href=&quot;/2026/01/15/10Thougths/&quot;&gt;random&lt;/a&gt; &lt;a href=&quot;/2026/01/29/RandomThoughts/&quot;&gt;thoughts&lt;/a&gt;, a few lines of code, or quick sketches, &lt;a href=&quot;/2024/12/14/Consistency/&quot;&gt;a small, imperfect repetition&lt;/a&gt; is better than breaking a habit.&lt;/p&gt;

&lt;p&gt;Your small reps won’t make you lose credibility. They prove that when life throws curveballs, your habit stays intact.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Future of coding, target bets, and SaaS</title>
   <link href="https://canro91.github.io/2026/01/30/FridayLinks/"/>
   <updated>2026-01-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/30/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;This week marks three months since I launched &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Street-Smart Coding&lt;/a&gt;. It started in a notebook with a 10-idea list. Now it’s a book. Getting the first paperback copies made me feel like a New York Times bestselling author. Pinch me, please.&lt;/p&gt;

&lt;p&gt;After that quick update, here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; For months, we’ve been hearing that AI will replace coders. But a more realistic prediction is to say that &lt;a href=&quot;https://alexop.dev/posts/developers-wont-write-code-by-hand/&quot;&gt;we won’t be writing code by hand in 5 years&lt;/a&gt; (10min). Truth is, coding was never the hard part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; If you’re looking for a tech job in 2026, instead of applying and sending out CVs everywhere, &lt;a href=&quot;https://www.seanmuirhead.com/blog/targeted-bets&quot;&gt;make a target bet&lt;/a&gt; (4min). Without knowing it, that’s the strategy I followed to land my last contracting gig.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; With LLMs and agents, building in-house replacements for paid tools sounds tempting as a way to save some money). But &lt;a href=&quot;https://priver.dev/blog/congratz-you-just-build-the-top-of-the-iceberg/&quot;&gt;writing code is just the tip of the iceberg&lt;/a&gt; (5min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; These days, American coworkers complain about the weather, and I keep converting between Fahrenheit and Celsius. Here’s &lt;a href=&quot;https://evanhahn.com/fahrenheit-celsius-heuristic/&quot;&gt;a quick mental heuristic to do it&lt;/a&gt; (1min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/01/26/AnotherAIRule/&quot;&gt;a new rule for using AI without losing my skills&lt;/a&gt; (2min) and &lt;a href=&quot;/2026/01/27/ExpertAtFailing/&quot;&gt;how to be an expert at failing (and survive to tell the story)&lt;/a&gt; (5min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;em&gt;Street-Smart Coding,&lt;/em&gt; 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until next Friday, keep coding smart&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Quick Thoughts on Creativity and Growth to End the Week</title>
   <link href="https://canro91.github.io/2026/01/29/RandomThoughts/"/>
   <updated>2026-01-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/29/RandomThoughts</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;#1. Always have something to write on.&lt;/strong&gt; This week, I caught up with an ex-coworker. He shared a valuable story that deserved &lt;a href=&quot;/2026/01/27/ExpertAtFailing/&quot;&gt;its own post&lt;/a&gt;. Thank goodness I grabbed a napkin from the cafe where we met. That’s where I outlined the post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. The notebook cult.&lt;/strong&gt; Finding out about zines to &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;reduce my phone time&lt;/a&gt; took me to a YouTube rabbit hole. Turns out there’s a whole cult. People with notebooks for everything. Notebooks for quotes, journaling, habit trackers, commonplace notebooks, pocket notebooks. I’m not joining the cult, I’m fine with &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;my idea pad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Start your next creative project backwards.&lt;/strong&gt; Before starting, imagine it’s finished, then write the sales page and announcements. That forces you to clarify your goal and message. That’s what I’m doing for my next book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. A simple method to overcome burnout.&lt;/strong&gt; A pen pal shared his burnout was appearing again. When he asked how I avoid burnout, I shared my mantra: care for your body, mind, and spirit daily. That’s &lt;a href=&quot;/2025/07/03/LifeChangingIdeas/&quot;&gt;one of the ideas that has changed my life&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. 1, 2, 3, and get up.&lt;/strong&gt; I can’t remember what podcast I learned this from. As soon as you wake up, count up to 3 and get up. Those moments after waking up are when you &lt;a href=&quot;/2024/12/19/TimeTravel/&quot;&gt;become a time traveler&lt;/a&gt;, reliving the past and sketching possible futures.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>An Update After Three Months of Launching Street-Smart Coding</title>
   <link href="https://canro91.github.io/2026/01/28/ThreeMonthUpdate/"/>
   <updated>2026-01-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/28/ThreeMonthUpdate</id>
   <content type="html">&lt;p&gt;Three months ago, I launched my first book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;. A persistence test.&lt;/p&gt;

&lt;p&gt;It wasn’t my first, but the first I was confident enough to call a book. It challenged my persistence. I even wrote chapters in a hospital while supporting a loved one. Chapter 17 is one of those. Sometimes I avoid rereading those chapters.&lt;/p&gt;

&lt;h2 id=&quot;some-lessons-realizations-and-hard-truths&quot;&gt;Some lessons, realizations, and hard-truths&lt;/h2&gt;

&lt;p&gt;Writing a book taught me many lessons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; &lt;a href=&quot;/2025/09/22/WritingABook/&quot;&gt;Interior design is not that hard&lt;/a&gt;. Reedsy does the heavy lifting. Or you can find your way with Word and a lot of Googling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Instead of a massive launch, think of a series.&lt;/strong&gt; That’s &lt;a href=&quot;/2025/09/30/PromotingABook/&quot;&gt;the best promotion strategy for your first book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Set a deadline.&lt;/strong&gt; A task can take as much time as we give it. Writing a book isn’t the exception. It took me about 4 months to finish the first draft. Here are &lt;a href=&quot;/2025/11/16/LaunchingABook/&quot;&gt;more lessons nobody told me about writing a book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Once your book is out, the game starts in your head.&lt;/strong&gt; That’s the fear of finding typos or getting bad reviews. That’s refreshing your sales dashboard and comparing your book to others. &lt;a href=&quot;/2025/11/22/HardTruthsWritingABook/&quot;&gt;Here are more realizations&lt;/a&gt;. Just focus on what you can control: work on the next one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. There’s always something you could have done better.&lt;/strong&gt; Once it’s out, it’s out. Your job was to write it, your audience’s job is to read it. Again, work on the next one.&lt;/p&gt;

&lt;h2 id=&quot;im-doing-this-differently&quot;&gt;I’m doing this differently&lt;/h2&gt;

&lt;p&gt;By no means, I’m an expert book writer. I only have two under my belt. But here’s what I’d do differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Start backwards.&lt;/strong&gt; Start with a promise, cover, sales page, and outline. That forces you to clarify your book promise and message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; &lt;a href=&quot;/2025/11/09/OneLine/&quot;&gt;Come up with a one-liner&lt;/a&gt;. That’s to summarize the core ideas and to make promotion easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Pre-sell it earlier.&lt;/strong&gt; “Do good work and people will come” is a lie.&lt;/p&gt;

&lt;p&gt;I once read an ebook called, &lt;em&gt;Start Marketing the Day You Start Coding&lt;/em&gt; by Rob Walling. It was about building SaaS startups, but the idea works for books too.&lt;/p&gt;

&lt;p&gt;With a clear promise, cover, and sales page (following #1), start promoting from day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. And I’d order my author copies from Amazon earlier.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;some-numbers&quot;&gt;Some numbers&lt;/h2&gt;

&lt;p&gt;I offer &lt;em&gt;Street-Smart Coding&lt;/em&gt; as an eBook via &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=three-month-update&quot;&gt;Gumroad&lt;/a&gt;, and in Kindle and paperback formats in &lt;a href=&quot;https://www.amazon.com/Street-Smart-Coding-Better-Without-Losing/dp/B0G64PG9NP&quot;&gt;Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of copies sold:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;eBook version: 51 sales. +2 in Spanish edition&lt;/li&gt;
  &lt;li&gt;Kindle version: 1 sale.&lt;/li&gt;
  &lt;li&gt;Paperback version: 1 sale.&lt;/li&gt;
  &lt;li&gt;“Door-to-door” sales: 8 copies. (7 Spanish, 1 English).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For me, even one sale beyond friends and acquaintances meant success. That’s enough motivation to keep writing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Be an Expert at Failing (and Survive to Tell the Story)</title>
   <link href="https://canro91.github.io/2026/01/27/ExpertAtFailing/"/>
   <updated>2026-01-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/27/ExpertAtFailing</id>
   <content type="html">&lt;p&gt;&lt;em&gt;“I’m not an expert at anything. Only at failing.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A friend and ex-coworker told me that when we caught up over coffee after many years. Well, I was giving him a paperback copy of &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;my latest book&lt;/a&gt;. Shameless plug.&lt;/p&gt;

&lt;p&gt;Our conversation immediately shifted after that line. I had to ask him why he said that.&lt;/p&gt;

&lt;h2 id=&quot;you-have-to-start-from-scratch&quot;&gt;You have to start from scratch&lt;/h2&gt;

&lt;p&gt;At college, everybody told him he had a talent for coding. But he was fired from his first job for his performance.&lt;/p&gt;

&lt;p&gt;After losing his job, he broke his wedding engagement. He disappointed his parents. He thought he had nothing else to do. Failure wasn’t a stranger. It had become his shadow.&lt;/p&gt;

&lt;p&gt;He started as a teacher. He thought he had found “his thing.” But he got fired from that too. Another disappointment.&lt;/p&gt;

&lt;p&gt;Then, to try something new, he went back to college. He had to find something. He had to find his calling.&lt;/p&gt;

&lt;p&gt;One day on campus, ready to quit, he sat at a table with his head in his hands. The pose of a disappointed, frustrated man.&lt;/p&gt;

&lt;p&gt;That’s when he heard something unexpected.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Which plant grows faster? A tomato plant or a mango tree?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“A tomato plant,”&lt;/em&gt; other students who had joined the table said.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Which plant gives fruit for longer?”&lt;/em&gt; the mysterious voice said. He kept his head down all that time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“The mango tree,”&lt;/em&gt; the students said.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Exactly! You have to be mango trees. The tomato plant dies after harvest. A mango tree gives mangoes for a lifetime.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After many failures, that was his wake-up call. At first, he thought it was a wise student. But when my friend raised his head to find out where that voice came from, he found a teacher.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“It was God,”&lt;/em&gt; he told me.&lt;/p&gt;

&lt;p&gt;That day, he decided to get up and work for himself. If his parents or family were disappointed because he didn’t meet their expectations, that was their problem. Not his.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 1:&lt;/strong&gt; Live up to your own expectations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 2:&lt;/strong&gt; You have to start over and over. That’s a skill no class teaches you, only failure and life do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 3:&lt;/strong&gt; Be a mango tree.&lt;/p&gt;

&lt;p&gt;That was a moment to start from scratch again, after failing at his first job, his first relationship, and first everything.&lt;/p&gt;

&lt;h2 id=&quot;be-good-at-making-an-extra-effort&quot;&gt;Be good at making an extra effort&lt;/h2&gt;

&lt;p&gt;Being fired from his first job was a sign he wasn’t as smart as he had thought.&lt;/p&gt;

&lt;p&gt;Maybe it was impostor syndrome. Trust me, he’s smart.&lt;/p&gt;

&lt;p&gt;But after months, he was interviewing at the same place where I was working.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I know I’m not the smartest, but I’m going to be the most charismatic,”&lt;/em&gt; that’s what he thought before the first interview.&lt;/p&gt;

&lt;p&gt;That strategy worked. He got the job. But failing at so many things, he was ready to get fired after the first month.&lt;/p&gt;

&lt;p&gt;Later, at my workplace, he carried a notebook and wrote down everything. When I asked him about it, he told me, &lt;em&gt;“I had to take away something, at least some notes.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“After many failures, I learned to read who was really someone who knew and who was just a charlatan. I learned from everyone and I wrote it down.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;He was at the office before clock-in and still there after clock-out. &lt;em&gt;“I had to make an extra effort. It took me 5 hours to finish what others did in 1.”&lt;/em&gt; Effort was his secret weapon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 4:&lt;/strong&gt; Take a notebook with you everywhere. (Thank goodness I had a napkin to write down the lessons my friend taught me as soon as our conversation was over.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 5:&lt;/strong&gt; Persistence beats intelligence. If you aren’t the smartest, you have to be the one who puts the most effort.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“A coding problem? Nah! That’s not a problem. I’m used to failure.”&lt;/em&gt; Sure, a compilation error was nothing after many disappointments and setbacks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“That’s why I say I’m an expert at failing.”&lt;/em&gt; Wow! We shook hands and went our separate ways. That day, I met a true expert and a wise man—and realized failing isn’t the end.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Another Rule for Using AI Without Losing My Skills</title>
   <link href="https://canro91.github.io/2026/01/26/AnotherAIRule/"/>
   <updated>2026-01-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/26/AnotherAIRule</id>
   <content type="html">&lt;p&gt;When code breaks, you can’t simply say, &lt;em&gt;“AI did that.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You’re responsible for the code you ship. That’s been true from the days of copy-pasting from forums, blog posts, StackOverflow, and now from AI.&lt;/p&gt;

&lt;p&gt;AI is fast, but over-reliance makes you lose your mental models and context—&lt;a href=&quot;/2025/12/01/TooMuchAI/&quot;&gt;just one problem with AI&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-new-rule&quot;&gt;The new rule&lt;/h2&gt;

&lt;p&gt;To protect my skills, &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;I’ve set one rule&lt;/a&gt;: &lt;strong&gt;Don’t let AI touch your code directly.&lt;/strong&gt; It might feel unproductive. But it keeps my hands on the wheel.&lt;/p&gt;

&lt;p&gt;To test this rule, I recently tried finishing a task using as much AI as possible. While going through the AI-generated code, I came up with another rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If I write the code, AI reviews it. And if AI generates the code, I review it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That way, I use AI while keeping my code writing and reading sharp. Either way, AI is just like a copilot in the cockpit, an extra layer of safety and productivity.&lt;/p&gt;

&lt;p&gt;AI is changing the act of coding. Some brag about coding without typing a single line of code, thanks to Claude Code. Whether what AI generates is clean code or garbage, its CEOs aren’t accountable for it. We are.&lt;/p&gt;

&lt;p&gt;If you want to sharpen the skills AI can’t replace, check out &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=another-rule-using-ai-without-losing-my-skills&quot;&gt;Street-Smart Coding&lt;/a&gt;. It’s the guide I wish I’d had on my journey to becoming a senior coder.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A 7-Word Trick for Better Book Notes</title>
   <link href="https://canro91.github.io/2026/01/25/7Words/"/>
   <updated>2026-01-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/25/7Words</id>
   <content type="html">&lt;p&gt;Books only matter if you act on them.&lt;/p&gt;

&lt;p&gt;The first step is to highlight, underline, or &lt;a href=&quot;/2026/01/20/WritingInBooks/&quot;&gt;write in margins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next step is to take notes. The problem is copying passages without processing them (or letting AI do it). &lt;a href=&quot;/2020/11/18/HowToTakeSmartNotes/&quot;&gt;The Zettelkasten method&lt;/a&gt; (and &lt;a href=&quot;/2024/05/13/HowToReadNonFictionBooks/&quot;&gt;my tweaked version&lt;/a&gt;) solves that by finding connections between notes.&lt;/p&gt;

&lt;p&gt;But today I found another note-taking idea: &lt;strong&gt;The 7-word summary&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After a chapter or section, or a whole book, write a 7-word summary. That forces you to condense big ideas into your words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; For better organization, keep your 7-word summaries in a single place.&lt;/p&gt;

&lt;p&gt;That’s a perfect partner to &lt;a href=&quot;/2025/06/22/SimplerNoteTaking/&quot;&gt;a 10-idea list&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Progress Report: Breaking My Phone Habit</title>
   <link href="https://canro91.github.io/2026/01/24/LessPhoneProgress/"/>
   <updated>2026-01-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/24/LessPhoneProgress</id>
   <content type="html">&lt;p&gt;Two days ago, I decided it was time to &lt;a href=&quot;/2026/01/22/LessScrolling/&quot;&gt;reduce my phone use&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I left books where I used to put my phone: desk, couch, and table. Instead of reaching for my phone, now I find myself opening &lt;em&gt;The Power of Now&lt;/em&gt; by Eckhart Tolle or &lt;em&gt;Jesus’ Son&lt;/em&gt; by Dennis Johnson.&lt;/p&gt;

&lt;p&gt;Yesterday, I paused on my way to my desk to grab my phone. Midway, I realized my phone wasn’t there anymore. That’s a habit being replaced.&lt;/p&gt;

&lt;p&gt;Today, I thought carefully about when I needed my phone. And it was easier to read with books nearby. I even left it behind for a quick errand.&lt;/p&gt;

&lt;p&gt;Big changes start with small actions. Mine began with swapping screens for pages.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Old laptops, meetings, and the vibe coding paradox</title>
   <link href="https://canro91.github.io/2026/01/23/FridayLinks/"/>
   <updated>2026-01-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/23/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there!&lt;/p&gt;

&lt;p&gt;This week I’m relaunching my C# unit testing intro book.&lt;/p&gt;

&lt;p&gt;During the holidays, I revamped &lt;em&gt;Unit Testing 101&lt;/em&gt; with clearer chapters, tighter explanations, and a fresh design.&lt;/p&gt;

&lt;p&gt;If you already bought it, you should have received the new version. Otherwise, &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot;&gt;grab it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, this week’s 4 interesting links:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; StackOverflow feels dead. Maybe a victim of its own success. And &lt;a href=&quot;https://idiallo.com/blog/we-were-never-good-programmers&quot;&gt;we were never good programmers&lt;/a&gt; (5min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; &lt;a href=&quot;https://tante.cc/2026/01/15/software-as-fast-fashion/&quot;&gt;AI is turning software (and coding) into fast fashion&lt;/a&gt; (6min). “Software is no longer seen as an asset… It’s a throw-away product. Like a napkin.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; New to a codebase? &lt;a href=&quot;https://ankursethi.com/blog/smallest-possible-change/&quot;&gt;Push the smallest possible change to production&lt;/a&gt; (3min), even a README tweak.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Cool tool of the week: &lt;a href=&quot;https://github.com/originalankur/maptoposter&quot;&gt;maptoposter&lt;/a&gt;. Turn your favorite city map into a beautiful poster.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Beyond coding, I wrote on my blog about &lt;a href=&quot;/2026/01/19/LinkedIn/&quot;&gt;how I use LinkedIn mindfully&lt;/a&gt; (3min) and &lt;a href=&quot;/2026/01/21/GlucoseSpikes/&quot;&gt;why you’re still hungry after eating and how to fix it&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… the new version of &lt;em&gt;Unit Testing 101&lt;/em&gt;. OK, I already told you about that. Check &lt;em&gt;Street-Smart Coding&lt;/em&gt;: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Simple Idea to Reduce My Phone Time</title>
   <link href="https://canro91.github.io/2026/01/22/LessScrolling/"/>
   <updated>2026-01-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/22/LessScrolling</id>
   <content type="html">&lt;p&gt;I tell myself I’m not addicted to my phone. That’s exactly what addicts say.&lt;/p&gt;

&lt;p&gt;This week, I averaged 2h39min on my phone. Today, it was 1h55min. I just checked it. That’s enough to &lt;a href=&quot;/2025/12/30/TwoHours/&quot;&gt;produce some creative work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my defense, I only listened to podcasts while doing the dishes. But most of that time was scrolling to find what to listen to.&lt;/p&gt;

&lt;p&gt;This year, my intention is simplicity: ditching my distraction machine to live lighter.&lt;/p&gt;

&lt;h2 id=&quot;replacing-a-bad-habit-with-a-good-one&quot;&gt;Replacing a bad habit with a good one&lt;/h2&gt;

&lt;p&gt;For that, I’m putting &lt;a href=&quot;/2024/12/13/KeepingPhonesAround/&quot;&gt;my phone out of sight&lt;/a&gt;, in a new place.&lt;/p&gt;

&lt;p&gt;And in every place where I used to put my phone, I’m leaving a book and a zine instead.&lt;/p&gt;

&lt;p&gt;This week, a rabbit hole took me to &lt;a href=&quot;https://austinkleon.substack.com/p/read-like-an-artist-db7&quot;&gt;Austen Kleon’s rules of reading&lt;/a&gt;. He made a zine, a tiny magazine with eight pages created from a single sheet of paper.&lt;/p&gt;

&lt;p&gt;And when you want to buy a red car, you see red cars everywhere. Today I discovered zines for free-form journaling and single-purpose notebooks. Credit goes to Cal Newport’s podcast for the notebook idea. Yes, I was doing the dishes.&lt;/p&gt;

&lt;p&gt;What if I combine the two ideas? Maybe one zine per day for quotes and lessons. And another per project for ideas and questions.&lt;/p&gt;

&lt;p&gt;That’s how I’m replacing scrolling with reading and even making my own zines.&lt;/p&gt;

&lt;p&gt;If you want to follow along, here are &lt;a href=&quot;/2025/12/31/Books/&quot;&gt;the books I read last year&lt;/a&gt;. Perfect replacements for where your phone used to sit.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why You&apos;re Still Hungry After Eating—And How to Fix It</title>
   <link href="https://canro91.github.io/2026/01/21/GlucoseSpikes/"/>
   <updated>2026-01-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/21/GlucoseSpikes</id>
   <content type="html">&lt;p&gt;Right after lunch today, I was already craving a snack.&lt;/p&gt;

&lt;p&gt;I usually don’t eat between meals. But today, I had to eat a cup of kefir with sunflower seeds. Minutes later, I had a slice of cheese. And I was still hungry.&lt;/p&gt;

&lt;p&gt;But that started a few hours earlier at lunch. I ate too many carbs. Rice was the culprit. I couldn’t resist. Where I live, eating rice is almost a national sport.&lt;/p&gt;

&lt;p&gt;The reason behind that is glucose spikes.&lt;/p&gt;

&lt;h2 id=&quot;glucose-revolution&quot;&gt;Glucose Revolution&lt;/h2&gt;

&lt;p&gt;After eating carbs or sugar, our glucose levels rise. Our body produces insulin to lower them. Then the crash comes. We feel tired, irritated, and hungrier.&lt;/p&gt;

&lt;p&gt;And when we feel hungry, we grab unhealthy snacks, causing more spikes. And the cycle continues. That’s why I was hungry earlier today. Chances are, you’ve felt the same..&lt;/p&gt;

&lt;p&gt;I’m not new to glucose spikes. Learning to control them has been &lt;a href=&quot;/2024/12/30/BestProductivityHack/&quot;&gt;my best productivity hack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My sister read &lt;em&gt;Glucose Revolution&lt;/em&gt; by Jessie Inchauspé first and shared tips I started applying.&lt;/p&gt;

&lt;p&gt;Finally, I picked up the book too. &lt;a href=&quot;/2025/12/31/Books/&quot;&gt;I started reading it last year&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;easy-hacks-to-reduce-glucose-spikes-starting-with-the-easiest-ones&quot;&gt;Easy hacks to reduce glucose spikes (starting with the easiest ones)&lt;/h2&gt;

&lt;p&gt;Here are some of the hacks I’m following to keep glucose spikes under control:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Eat a salad (or some greens) with every meal.&lt;/li&gt;
  &lt;li&gt;Eat salad first, then proteins, then carbs. In that order.&lt;/li&gt;
  &lt;li&gt;Move your body after eating. Take a walk or do some squats. But move!&lt;/li&gt;
  &lt;li&gt;Say good-bye to sugar. If ego is your enemy, sugar is your arch-enemy. It’s the devil.&lt;/li&gt;
  &lt;li&gt;Eat as many greens as carbs. A hamburger has the right ingredients, but in the wrong amounts.&lt;/li&gt;
  &lt;li&gt;Drink water with apple cider vinegar 30 minutes before lunch.&lt;/li&gt;
  &lt;li&gt;Prefer salty snacks over candy or sweets.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The book explains each hack with research. I won’t pretend I remember all the details. Greens first makes your body absorb fiber first, slowing down carbs absorption.&lt;/p&gt;

&lt;p&gt;If you’re looking for a 20/80 rule, follow #1 and #2. That’s how I dropped from ~80KG to ~74KG in a couple of months and now I don’t drag my feet to get over my afternoons.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why You Should Write and Underline in Books</title>
   <link href="https://canro91.github.io/2026/01/20/WritingInBooks/"/>
   <updated>2026-01-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/20/WritingInBooks</id>
   <content type="html">&lt;p&gt;Recently, I found a book my mom left behind. It felt like opening a time capsule.&lt;/p&gt;

&lt;p&gt;It’s &lt;em&gt;Secrets to Thrive in Life&lt;/em&gt; (or &lt;em&gt;Secretos para triunfar en la vida&lt;/em&gt; in Spanish). She always mentioned that book.&lt;/p&gt;

&lt;p&gt;It’s a collection of maxims, proverbs, and passages from other personal development books. On the first page, my mom wrote her name and a date. She was likely in her 20s.&lt;/p&gt;

&lt;p&gt;She underlined sentences and starred titles. With a blue pen and ruler, she drew perfect lines. I picture her lying on her bed, reading with an orange translucent ruler.&lt;/p&gt;

&lt;p&gt;Opening the book felt like uncovering her thoughts and circumstances. She underlined a passage about dealing with people and dating. She must have been going out with my dad.&lt;/p&gt;

&lt;p&gt;I was reluctant to write in books. That felt like a capital sin. Then I opened her book and felt she was sharing success clues.&lt;/p&gt;

&lt;p&gt;I want to do the same for my future kids and grandkids. I’m not afraid of writing in books anymore. Writing in margins, starring titles, and underlining passages turn books into time capsules of our thoughts. A legacy for future generations.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I Use LinkedIn Mindfully (In 5 Simple Steps)</title>
   <link href="https://canro91.github.io/2026/01/19/LinkedIn/"/>
   <updated>2026-01-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/19/LinkedIn</id>
   <content type="html">&lt;p&gt;One day at the office, I created my LinkedIn account to escape that job.&lt;/p&gt;

&lt;p&gt;That was five or six years ago. That afternoon, my coworkers and I made it a group activity. We endorsed each other’s skills and exchanged recommendations. We were planning our escape route because things were getting tough.&lt;/p&gt;

&lt;p&gt;I didn’t find my next job on LinkedIn. But I made my first internet money there. And that kept me going back.&lt;/p&gt;

&lt;p&gt;In 2024, I revived my account after months of inactivity. After landing a new job, I stopped logging into LinkedIn.&lt;/p&gt;

&lt;p&gt;Since then, I’ve written over &lt;a href=&quot;/2025/06/15/LinkedIn/&quot;&gt;300 posts&lt;/a&gt;. I’ve &lt;a href=&quot;/2025/10/02/ConsistencyIsKey/&quot;&gt;preached the same overused cliches&lt;/a&gt;, &lt;a href=&quot;/2025/07/27/LinkedInWeirdness/&quot;&gt;stumbled upon weird situations&lt;/a&gt;, and learned that &lt;a href=&quot;/2025/09/28/TooLong/&quot;&gt;people don’t read, but skim online&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From job hunting to posting daily, to &lt;a href=&quot;/2025/08/05/LinkedInStrategy/&quot;&gt;ignoring all gurus’ growth advice&lt;/a&gt;, I’ve learned meaningful use matters more than virality.&lt;/p&gt;

&lt;h2 id=&quot;5-steps-to-use-linkedin-more-consciously-ignoring-growth-hacks&quot;&gt;5 steps to use LinkedIn more consciously (ignoring growth hacks)&lt;/h2&gt;

&lt;p&gt;Here’s what I do to use LinkedIn more consciously:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Use it with a timer.&lt;/strong&gt; Like other platforms, it’s designed to trap you. One notification can spiral into hours of doomscrolling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Curate your feed.&lt;/strong&gt; Your feed is a rabbit hole.&lt;/p&gt;

&lt;p&gt;To curate it, follow:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;People you know in real life&lt;/li&gt;
  &lt;li&gt;People you want to connect with&lt;/li&gt;
  &lt;li&gt;People sharing interesting ideas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Avoid scrolling down your feed by bookmarking profiles you want to interact with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Share interesting things.&lt;/strong&gt; Nobody cares about another Udemy certificate. Use it to share your learning or resources you find useful. Finished an interesting book? Share what you learned with a photo of the cover. Working on a project? Share a lesson learned.&lt;/p&gt;

&lt;p&gt;And please, please &lt;a href=&quot;/2025/11/30/TheOnlyWritingGoal/&quot;&gt;don’t try to sound corporate&lt;/a&gt;. If you’re typing &lt;em&gt;“Dear LinkedIn network,”&lt;/em&gt; stop and start again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Keep in touch with your network.&lt;/strong&gt; Send quick “Hello” to old connections: bosses, coworkers, recruiters… Don’t use AI. Keep it as human as possible. &lt;em&gt;“What are you up to these days?”&lt;/em&gt; works perfectly fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Use comments wisely.&lt;/strong&gt; Most influencers preach about commenting more and more like the ultimate growth hack. That’s another rabbit hole.&lt;/p&gt;

&lt;p&gt;Use comments to start genuine conversations instead. Reply to interesting comments and follow up with connection requests. &lt;em&gt;“Hey, your comment about XYZ got my attention…“&lt;/em&gt; Again, be human. That’s how I met a top cellist, an award-winning teacher, a speaking coach, and many more interesting people.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Handle Too Many Creative Ideas</title>
   <link href="https://canro91.github.io/2026/01/18/TooManyIdeas/"/>
   <updated>2026-01-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/18/TooManyIdeas</id>
   <content type="html">&lt;p&gt;One problem is having no ideas. Another is having far too many.&lt;/p&gt;

&lt;p&gt;With endless options, which project comes next?&lt;/p&gt;

&lt;p&gt;That was one of the questions Austen Kleon, author of &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt;, got in &lt;a href=&quot;https://youtu.be/AvUELPpj63s?t=3616&quot;&gt;this interview&lt;/a&gt;. An audience member with “millions of ideas” and “29 books to write” asked how to find time for them.&lt;/p&gt;

&lt;p&gt;Her struggle resonated with me. Because these days, I’m like her. I have too many books and projects to choose from.&lt;/p&gt;

&lt;p&gt;But here’s what I’ve been trying when I feel overwhelmed by ideas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Write down every idea as it comes.&lt;/strong&gt; A book title, a concept, or just a phrase. Don’t let them go away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Create distance from your ideas.&lt;/strong&gt; Take some time to do something else. This space helps your brain to connect the dots.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Choose wisely.&lt;/strong&gt; Tackle the project you can finish the fastest or the one you’re most excited about.&lt;/p&gt;

&lt;p&gt;Credits to my sister for helping me think this through.&lt;/p&gt;

&lt;p&gt;Currently I’m at phase one: brainstorming with &lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;10-idea lists&lt;/a&gt;. Meanwhile, I’m giving my brain space to make connections by doing nothing. I know there’s a book in there. I’m trusting the process.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Some Days You Just Have to Stop and Do Nothing</title>
   <link href="https://canro91.github.io/2026/01/17/DoNothing/"/>
   <updated>2026-01-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/17/DoNothing</id>
   <content type="html">&lt;p&gt;Earlier today, I crossed the city for some blood tests. Regular checkups. Hot and sunny outside.&lt;/p&gt;

&lt;p&gt;I don’t know if it was the heat, too much information, or just too much on my mind. (I even had to do &lt;a href=&quot;/2026/01/15/10Thougths/&quot;&gt;a brain dump&lt;/a&gt; recently.) But I had to make lemon water, put on my headphones with ocean waves, and sit to do nothing else.&lt;/p&gt;

&lt;p&gt;Maybe that’s what I needed to get over a creative block. Sometimes you need to do nothing. Not less. Nothing.&lt;/p&gt;

&lt;p&gt;If you’re reading this, try it: put down your phone or your computer, and let yourself do nothing. It might clear your block too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Old laptops, meetings, and the vibe coding paradox</title>
   <link href="https://canro91.github.io/2026/01/16/FridayLinks/"/>
   <updated>2026-01-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/16/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Curious about laptops 30 years ago? Here’s &lt;a href=&quot;https://blog.hayman.net/2026/01/09/i-checked-my-notebook.html&quot;&gt;the “I checked my notebook” commercial&lt;/a&gt; (1min). My laptop wouldn’t survive that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; I’ve used StackOverflow (and still do), but never created an account or asked a question. Not a welcoming community. That’s &lt;a href=&quot;https://www.pcloadletter.dev/blog/abandoning-stackoverflow/&quot;&gt;why StackOverflow is dead&lt;/a&gt; (3min). ChatGPT doesn’t close your questions or downvote them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Meetings are the worst part of coding professionally. Especially those when the organizer is late, everybody waits…awkward silence…Here’s &lt;a href=&quot;https://philipotoole.com/start-your-meetings-at-5-minutes-past/&quot;&gt;a quick hack to start meetings on time&lt;/a&gt; (2min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; I keep seeing people online brag about coding without touching an editor or IDE. Now they “code” from their phones with Claude Code. Vibe coding was bad. But now coding outside an editor is good? That’s the &lt;a href=&quot;https://blog.kaplich.me/vibe-coding-paradox/&quot;&gt;vibe coding paradox&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/12/31/Books/&quot;&gt;the books I read and the ones I didn’t finish in 2025&lt;/a&gt; (3min) and &lt;a href=&quot;/2026/01/04/CodeCantSpeak/&quot;&gt;why we should forget about LOCs to stand out&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… my new book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Random Thoughts (on Focus, Creativity, and Coding) to Clear My Mind</title>
   <link href="https://canro91.github.io/2026/01/15/10Thougths/"/>
   <updated>2026-01-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/15/10Thougths</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Following my own advice, I’m writing a 10-idea list to &lt;a href=&quot;/2025/04/19/CalmDown/&quot;&gt;quiet the chatter in my head&lt;/a&gt;. Not that I had writer’s block, but a “too much noise in my head” block—If that’s even a thing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Use your blog as insurance.&lt;/strong&gt; &lt;a href=&quot;/2025/05/02/Blogging/&quot;&gt;Blogs might feel dead&lt;/a&gt;. People consume content inside platforms, instead of Googling to land on personal blogs. But your blog is your insurance against dead platforms and suspensions. If you trigger a filter inside a platform, even if it’s a false positive, your account gets suspended, with almost no chance of getting it back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Your real asset is your email list.&lt;/strong&gt; I’ve heard it plenty of times: &lt;em&gt;“Build your list…,”&lt;/em&gt; &lt;em&gt;“Count subscribers, not followers…“&lt;/em&gt; But it wasn’t until my account on a social blog got suspended that I learned the lesson.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Cut the news.&lt;/strong&gt; Venezuela, Iran, Israel, United States…There’s always something going on somewhere. Unless you’re a diplomat, advisor, or spy, there’s almost nothing you can do. News only makes you anxious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. &lt;em&gt;“I want to build a product.”&lt;/em&gt;&lt;/strong&gt; This is the second or third time I’ve heard a friend saying they want to build something beyond the 9-5. The problem? They recreate their day job after hours, when there’s less energy and willpower. A better alternative is to make experiments and &lt;a href=&quot;/2025/12/30/TwoHours/&quot;&gt;create something in two hours&lt;/a&gt;. Or &lt;a href=&quot;/2025/07/06/SmallBets/&quot;&gt;a portfolio of small bets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. We live without being conscious of our bodies.&lt;/strong&gt; I’m rereading &lt;em&gt;The Power of Now&lt;/em&gt; by Eckhart Tolle. I wasn’t ready the first time I read it. One takeaway so far: We’re so in our heads we forget our bodies. Just stop to breathe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Coding is changing for good.&lt;/strong&gt; I keep seeing people on the IndieWeb and LinkedIn brag about coding without touching an editor or IDE. Now they “code” from their phones with Claude Code. Maybe &lt;a href=&quot;/2026/01/04/CodeCantSpeak/&quot;&gt;writing lines of code is becoming optional&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Write books for AI to read.&lt;/strong&gt; The other day, Kevin Kelly said he writes (or plans to write) his books for AI. What if we offer our books as chatbots? Instead of reading it, people talk with them. It’s another reminder that our job as writers is to adapt to how our readers consume content. Credits to &lt;a href=&quot;/2026/01/07/SmartBrevity/&quot;&gt;Smart Brevity&lt;/a&gt;. That’s how we’re consuming content now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. 12 apps in a year.&lt;/strong&gt; The other day, I found an indie blogger running a challenge: building one small app per month. Such a great idea! Forget the next Uber, build tiny apps (or websites) that solve a simple problem and put a “Buy me a Coffee” link. Something like a “pill time calculator” or “time off finder.” Those are two small apps I’d use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Do something hard this year.&lt;/strong&gt; This idea comes from Ryan Holiday. Marcus Aurelius, or another stoic, used to jump into a frozen lake at the start of every year. Prove yourself wrong and do something hard. Maybe that’s a side project, a book, or a jump into a frozen lake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. You’re not too late.&lt;/strong&gt; I just listened to an interview with Hassan Osman, a book writer, course creator, and podcaster. He admitted part of the success of his books and courses was starting around the lockdown in 2020. The best time was five years ago. The next best time to take action is now. Experiment, and double down.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Thought I Had Seen Short Books—Until I Found This One</title>
   <link href="https://canro91.github.io/2026/01/14/TheShortestBook/"/>
   <updated>2026-01-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/14/TheShortestBook</id>
   <content type="html">&lt;p&gt;For writers, the real challenge is to make readers finish books.&lt;/p&gt;

&lt;p&gt;TikTok, Instagram, and endless feeds have turned us into skimmers. And as &lt;a href=&quot;/2026/01/07/SmartBrevity/&quot;&gt;Smart Brevity&lt;/a&gt; says, our job as writers is to adapt to how readers consume content. &lt;a href=&quot;/2025/10/19/WritingPrinciples/&quot;&gt;Give them something fast&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Short non-fiction books, or &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;mini books&lt;/a&gt;, are the answer for impatient readers.&lt;/p&gt;

&lt;p&gt;A book doesn’t need 30,000 words. It could be &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;a summary of 10 papers&lt;/a&gt;, &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;a collection of personal stories&lt;/a&gt;, or your best posts—as long as it’s valuable even for one person, it’s worth publishing.&lt;/p&gt;

&lt;p&gt;And to my surprise, after going down a rabbit hole, today I found a one-page book, &lt;em&gt;This Book is One Page Long&lt;/em&gt; by Hassan Osman. Well, the book has 5 pages. I guess that’s the front matter, back matter, and one page of actual content.&lt;/p&gt;

&lt;p&gt;If that doesn’t kill every excuse for writing a book, nothing will.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: What Entity Framework Core Do With Nullable Foreign Keys</title>
   <link href="https://canro91.github.io/2026/01/13/NullableJoiningProperty/"/>
   <updated>2026-01-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/13/NullableJoiningProperty</id>
   <content type="html">&lt;p&gt;In another episode of &lt;em&gt;Adventures with Entity Framework…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I expected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Include()&lt;/code&gt; to always translate to an INNER JOIN. But with nullable “joining” properties, EF Core uses a LEFT JOIN.&lt;/p&gt;

&lt;p&gt;Here’s my replicated scenario with movies and directors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Let’s create a Movies and Directors table with no foreign keys between them.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Directors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* &amp;lt;- A nullable column here */&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* And no constraint here. I&apos;m a legacy app */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;#2. Let’s store (and retrieve) one movie and its director and an orphan director.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NullableForeignKeys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Directors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Director&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// A nullable property here&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Directors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NullableForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Server=(localdb)\\MSSQLLocalDB;Database=Movies;Trusted_Connection=True;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContextOptionsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseSqlServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// An orphan director&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Quentin Tarantino&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// A movie and its director&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Directors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;James Cameron&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Imagine a query with filters on director and movie&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directors&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// I thought this would retrieve&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// directors with movies&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToListAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// This one breaks...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this legacy app, child entities could exist without parents. The real query also applied filters on both entities.&lt;/p&gt;

&lt;p&gt;Now let’s look at the SQL EF Core generated:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice the LEFT JOIN. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int Movie&lt;/code&gt; instead produces an INNER JOIN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; JOIN type depends on the joining property’s nullability, not on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Include()&lt;/code&gt; itself.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>One Simple Shift That Turned My Content Into Sales</title>
   <link href="https://canro91.github.io/2026/01/12/SimpleSales/"/>
   <updated>2026-01-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/12/SimpleSales</id>
   <content type="html">&lt;p&gt;I tried the “proven” formula the big names swear by and it failed miserably.&lt;/p&gt;

&lt;p&gt;I did what most people recommend to sell:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Write social media and long-form posts.&lt;/li&gt;
  &lt;li&gt;Offer a free email course or opt-in.&lt;/li&gt;
  &lt;li&gt;Take people to a newsletter.&lt;/li&gt;
  &lt;li&gt;Pitch your offer or products.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But after months of content, I realized something was broken.&lt;/p&gt;

&lt;h2 id=&quot;the-realization-that-changed-everything&quot;&gt;The realization that changed everything&lt;/h2&gt;

&lt;p&gt;I packed my best career LinkedIn posts into &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=one-simple-shift-that-turned-my-content-sales&quot;&gt;a free 7-day email course&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I offered it as “pay what you want” and promoted it on LinkedIn. A few readers left a tip. It gave me momentum to keep promoting it.&lt;/p&gt;

&lt;p&gt;But when I sent emails, almost nobody clicked my offers. After more than 30 emails, my products made just $1. OK, maybe my copywriting wasn’t strong or I was selling the wrong products.&lt;/p&gt;

&lt;p&gt;The math doesn’t add up.&lt;/p&gt;

&lt;h2 id=&quot;the-change-that-brought-the-sales&quot;&gt;The change that brought the sales&lt;/h2&gt;

&lt;p&gt;Every step between your reader and your offer makes a sale harder.&lt;/p&gt;

&lt;p&gt;It’s like walking people into your store, then sending them away for a course before selling what’s already on your shelves. By the time you follow up, they already bought somewhere else and forgot about you.&lt;/p&gt;

&lt;p&gt;For my content strategy, I removed the intermediate steps. Social media and long-form posts -&amp;gt; products. Now I plug my book sales pages inside or at the end of posts.&lt;/p&gt;

&lt;p&gt;With that simple fix, sales came in and I passed &lt;a href=&quot;/2025/10/12/1DollarTest/&quot;&gt;the $1 test&lt;/a&gt;. My first book, &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;, got two pre-sales within weeks of announcing it.&lt;/p&gt;

&lt;p&gt;It wasn’t emails or “nurturing.” It was removing friction. A confused reader takes no action.&lt;/p&gt;

&lt;p&gt;Remove the friction. Make action effortless. &lt;a href=&quot;/2025/10/11/ContentThatSells/&quot;&gt;Let every piece of content be a sales representative&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Short Story to Become Wiser (and Be More Present)</title>
   <link href="https://canro91.github.io/2026/01/11/BecomeWiser/"/>
   <updated>2026-01-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/11/BecomeWiser</id>
   <content type="html">&lt;p&gt;One day, a man approached a Zen master with a question.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Master, what can a simple man like me do to become wiser like you?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Zen master said, &lt;em&gt;“Well, I just sleep, eat, and talk.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Hmm, I already do that. But I’m not a wise man like you,”&lt;/em&gt; the man said.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“You may do that. But when I sleep, I’m simply sleeping. When I eat, I’m simply eating. And when I talk, I’m simply talking. When you sleep, you remember problems. When you eat, you use your phone. And when you talk, you think about what to ask next or how to answer.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I first heard that story in a Sunday sermon, and it stayed with me.&lt;/p&gt;

&lt;p&gt;It reminds me of a lesson from Eckhart Tolle’s &lt;em&gt;The Power of Now&lt;/em&gt;. Our mind is like &lt;a href=&quot;/2024/12/19/TimeTravel/&quot;&gt;a time machine&lt;/a&gt;, taking us to the past (where guilt and resentment live) and into the future (where anxiety lives). But wisdom begins when we step off that machine and live in the present.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>2026 Start-of-Year Stats to Keep Myself Accountable</title>
   <link href="https://canro91.github.io/2026/01/10/AccountabilityStats/"/>
   <updated>2026-01-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/10/AccountabilityStats</id>
   <content type="html">&lt;p&gt;For the first time, I’m putting my numbers out there.&lt;/p&gt;

&lt;p&gt;Mark Thompson shared his stats &lt;a href=&quot;https://articles.thesystematicwriter.com/accountability-first-where-i-stand-lets-grow-from-here-70a03bac5a35?sk=a9b2b3818b6843e5758115ab709cd9a7&quot;&gt;on Medium&lt;/a&gt;. The point wasn’t to show off, but to hold ourselves accountable with our growing goals.&lt;/p&gt;

&lt;h2 id=&quot;here-are-some-of-my-stats-starting-2026&quot;&gt;Here are some of my stats, starting 2026:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. Blog:&lt;/strong&gt; My blog is &lt;a href=&quot;/2024/12/15/BloggingWorkflow/&quot;&gt;my content hub&lt;/a&gt;. All my ideas start or end on my blog. Then, I syndicate in other places.&lt;/p&gt;

&lt;p&gt;Total so far: 708 posts. Last year, I posted daily and hit the &lt;a href=&quot;/2025/12/06/400DailyPosts/&quot;&gt;400-daily posts milestone&lt;/a&gt;. For a recap, here’s my &lt;a href=&quot;/2026/01/01/BestOf2025/&quot;&gt;2025 in review&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Medium:&lt;/strong&gt; This is my main driver of traffic and book sales. Last year, I saw a big bump in views and followers. So far: 1,197 followers and 643 subscribers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. LinkedIn:&lt;/strong&gt; This is my only social media channel, where I reshare bite-sized posts. So far: 832 followers and 364 connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. dev.to:&lt;/strong&gt; I use dev.to only for coding content.&lt;/p&gt;

&lt;p&gt;I have a “funny” follower count: 28,552. Mostly inactive accounts. When you create a new account, the welcome wizard asks you to choose tags and follow people.&lt;/p&gt;

&lt;p&gt;More accurate stats: 236 posts, 1,921 reactions, and 549 comments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Gumroad:&lt;/strong&gt; This is my store for &lt;a href=&quot;https://imcsarag.gumroad.com/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=start-year-stats-keep-accountable&quot;&gt;courses and books&lt;/a&gt;. Last year:  202 sales, 3,530 views, and &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=start-year-stats-keep-accountable&quot;&gt;one new book launched&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Friday Links:&lt;/strong&gt; This is the “backend” for my product sales. Each Friday, I send 4 curated links. So far: 146 subscribers and 53 issues sent.&lt;/p&gt;

&lt;h2 id=&quot;my-plan-for-2026&quot;&gt;My plan for 2026&lt;/h2&gt;

&lt;p&gt;In 2026, I’m focusing on simplifying my content strategy, not chasing followers.&lt;/p&gt;

&lt;p&gt;Here’s what I’m doing:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;I’m using dev.to API to automatically repost my posts with a simple script.&lt;/li&gt;
  &lt;li&gt;I’m simplifying my &lt;a href=&quot;/2025/10/11/ContentThatSells/&quot;&gt;promotion strategy&lt;/a&gt; for my books.&lt;/li&gt;
  &lt;li&gt;I’m repurposing Medium highlights on LinkedIn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2026, I’m doubling down on a simple, repeatable system instead of vanity metrics.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: The best time to be a coder</title>
   <link href="https://canro91.github.io/2026/01/09/FridayLinks/"/>
   <updated>2026-01-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/09/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there!&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; AI makes coding and shipping nearly free. &lt;a href=&quot;https://medium.com/@iamalvisng/your-side-project-wont-save-you-anymore-ca997028227e?sk=d56cf519b83e4d1081a0014b2555d401&quot;&gt;Side projects alone won’t help you stand out&lt;/a&gt; (7min). Maybe it’s time to double down on open source contributions or start a “learn and build in public” YouTube channel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Addy Osmani, a leader at Google, shared &lt;a href=&quot;https://addyosmani.com/blog/21-lessons/&quot;&gt;21 lessons after a decade of coding&lt;/a&gt; (10min). The one that resonated the most? #6. &lt;em&gt;“Coding doesn’t advocate for you.”&lt;/em&gt; Good code alone doesn’t make you a good coder, especially in the AI era.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; AI shows coding isn’t the bottleneck. A concept that isn’t new. Here’s a breakdown of why &lt;a href=&quot;https://blog.robbowley.net/2026/01/05/coding-has-never-been-the-bottleneck/&quot;&gt;it’s never been the bottleneck&lt;/a&gt; (6min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Maybe juniors won’t say the same, but &lt;a href=&quot;https://zarar.dev/its-a-great-time-to-be-a-software-engineer/&quot;&gt;it’s a great time to be a coder&lt;/a&gt; (5min). Our job may be to design systems and let AI fill in the blanks. (That’s point #10 from that post)&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2026/01/06/HiringIsBrokenLaw/&quot;&gt;the “hiring is broken” law of coding blogs&lt;/a&gt; (2min) and &lt;a href=&quot;/2026/01/05/Mantra/&quot;&gt;the mantra to thrive in the AI era&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… my new book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding in 2026!&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>AI Means You Don&apos;t Need to Learn So Many Programming Languages</title>
   <link href="https://canro91.github.io/2026/01/08/FewerLanguages/"/>
   <updated>2026-01-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/08/FewerLanguages</id>
   <content type="html">&lt;p&gt;We no longer need to keep learning new programming languages.&lt;/p&gt;

&lt;p&gt;If AI writes 90% of our code, becoming a polyglot coder isn’t valuable anymore.&lt;/p&gt;

&lt;p&gt;Here’s what Gergely Orosz says in &lt;a href=&quot;https://newsletter.pragmaticengineer.com/p/when-ai-writes-almost-all-code-what&quot;&gt;When AI Writes All Code&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;…With AI writing most of the code, the advantage of knowing several languages will become less important when any engineer can jump into any codebase and ask the AI to implement a feature – which it will probably take a decent stab at. Even better, you can ask AI to explain parts of the codebase and quickly pick up a language much faster than without AI tools.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Obsessing over learning languages didn’t work for me. That was before AI.&lt;/p&gt;

&lt;p&gt;Trying to master many languages was &lt;a href=&quot;/2025/10/21/BiggestMistake/&quot;&gt;my biggest mistake as a new coder&lt;/a&gt;. Something else always made &lt;a href=&quot;/2026/01/04/CodeCantSpeak/&quot;&gt;people stand out&lt;/a&gt;. And something else &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;got me into trouble&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The real question is what skills matter.&lt;/p&gt;

&lt;p&gt;Maybe AI is making us:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Master one general-purpose language, like C or Go.&lt;/li&gt;
  &lt;li&gt;Understand core principles: problem decomposition, clean code, and SOLID.&lt;/li&gt;
  &lt;li&gt;Develop strong &lt;a href=&quot;/2025/03/26/ReadCode/&quot;&gt;code-reading skills&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or maybe we should learn languages that challenge our thinking: Haskell, LISP, or a language from a different paradigm. Learning Java after C# doesn’t teach much. AI can generate code in “challenging” languages, but they build &lt;a href=&quot;/2025/10/27/ProblemSolving/&quot;&gt;stronger problem-solving skills&lt;/a&gt; for prompting LLMs.&lt;/p&gt;

&lt;p&gt;To stand out when AI shines at coding, we need to step beyond the IDE. We need teamwork, communication, and the broader skills I cover in &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=ai-means-you-dont-need-learn-many-programming-languages&quot;&gt;Street-Smart Coding&lt;/a&gt;. It’s the roadmap I wish I had when starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Six Lessons to Say More With Fewer Words—Using Smart Brevity</title>
   <link href="https://canro91.github.io/2026/01/07/SmartBrevity/"/>
   <updated>2026-01-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/07/SmartBrevity</id>
   <content type="html">&lt;p&gt;As writers, your job is to adapt to how your readers consume words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Content is free and abundant. We are drowning in emails, Slack messages, and beeps and buzzes. In seconds, we decide to read or keep scrolling.&lt;/p&gt;

&lt;p&gt;A wall of text makes us stop reading. &lt;a href=&quot;/2025/09/28/TooLong/&quot;&gt;I learned that the hard way&lt;/a&gt;. And you and I do the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We don’t read, but skim.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deliver fast. Say more with fewer words. That’s the big idea behind &lt;em&gt;Smart Brevity,&lt;/em&gt; the digital version of the classic &lt;em&gt;Elements of Style&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;here-are-six-lessons-to-apply-smart-brevity&quot;&gt;Here are six lessons to apply Smart Brevity:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. The one idea test.&lt;/strong&gt; Start writing the one thing you want readers to know. Put it front and center. Then ask someone else if they can find it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Use headline, strong first line, “why,” and “go deeper.”&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Don’t make people choose what’s important. You tell them.&lt;/li&gt;
  &lt;li&gt;Tell readers what your piece is about and if it’s for them.&lt;/li&gt;
  &lt;li&gt;Use key phrases to guide skimmers. Phrases like &lt;em&gt;“why this matters,”&lt;/em&gt; &lt;em&gt;“the big picture,”&lt;/em&gt; or &lt;em&gt;“backstory.”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;a href=&quot;/2026/01/03/Mastery/&quot;&gt;Mastery&lt;/a&gt;, Robert Greene used “Understand” to introduce big ideas. That’s a visual clue for skimmers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. The bar &amp;amp; beach test.&lt;/strong&gt; Write like a human. Don’t use any word you wouldn’t use in a bar or on the beach. No more &lt;em&gt;“Dear LinkedIn network, I’m pleased to announce…“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. The “would you read it?” test.&lt;/strong&gt; Once you’re done, ask yourself: &lt;em&gt;“Would I read it if I hadn’t written it?”&lt;/em&gt; If not, why do you think someone else will?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. For email subject lines:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Use 6 words and prefer one-syllable words.&lt;/li&gt;
  &lt;li&gt;Emojis make your subject lines stand out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;#6. For presentations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Start and end with &lt;em&gt;“if you remember one thing from this, …“&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Use 5 or 6 slides and one point per slide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For fiction, we want authors to take us to new, imaginary worlds. But for emails, presentations, and social media posts, we need shorter, clear text. Even in non-fiction, why make readers flip through pages for one points? Brevity always wins!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The &quot;Hiring Is Broken&quot; Law of Coding Blogs</title>
   <link href="https://canro91.github.io/2026/01/06/HiringIsBrokenLaw/"/>
   <updated>2026-01-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/06/HiringIsBrokenLaw</id>
   <content type="html">&lt;p&gt;If you have a coding blog, sooner or later you’ll write a “hiring is broken” post.&lt;/p&gt;

&lt;p&gt;Today on r/programming, I saw another one. &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;I’ve written mine too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yes, we don’t know how to hire. Behavioral questions, LeetCode, &lt;a href=&quot;/2021/11/22/CodingChallengeTips/&quot;&gt;take-homes&lt;/a&gt;, IQ tests…Every company has a different answer.&lt;/p&gt;

&lt;p&gt;But our “hiring is broken” posts won’t change how big corporations interview. Decision-makers don’t read coders’ blogs.&lt;/p&gt;

&lt;p&gt;Instead of venting into the void, why not share “here’s what I learned and what I wish I had done differently…” posts, including:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The kind of interview&lt;/li&gt;
  &lt;li&gt;Subjects you forgot to review&lt;/li&gt;
  &lt;li&gt;What you like and dislike about the process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep blogging. One day, &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;your blog becomes your portfolio&lt;/a&gt;, and interviews turn into discussions about your posts. That has happened to me only once.&lt;/p&gt;

&lt;p&gt;Blogging won’t fix hiring overnight. But it can fix how you stand out. That’s why it’s one of the lessons in &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=hiring-broken-law&quot;&gt;Street-Smart Coding&lt;/a&gt;, the roadmap I wish I had staring out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Mantra to Thrive in the AI Hype</title>
   <link href="https://canro91.github.io/2026/01/05/Mantra/"/>
   <updated>2026-01-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/05/Mantra</id>
   <content type="html">&lt;p&gt;&lt;em&gt;If AI can do it in minutes, it’s not special.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AI has made coding accessible. You don’t need a degree or bootcamp to have something working. A few prompts to an LLM can replace hours or days of work.&lt;/p&gt;

&lt;p&gt;That’s good news…and bad news.&lt;/p&gt;

&lt;p&gt;A portfolio with the same to-do apps no longer stands out. Anyone can do that with AI in a few minutes. Maybe the alternative is a “build/learn in public” YouTube channel. Or contributing to non-trivial open source projects.&lt;/p&gt;

&lt;p&gt;A take-home challenge doesn’t work for hiring. Again. An easy task for AI. Maybe whiteboarding interviews won’t go away.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2026/01/04/CodeCantSpeak/&quot;&gt;Only crafting clean code isn’t enough&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don’t burn your copy of &lt;em&gt;Clean Code.&lt;/em&gt; You still need to tell whether what AI spits out is good code.&lt;/p&gt;

&lt;p&gt;But &lt;a href=&quot;/2025/12/21/Offerings/&quot;&gt;you need to do what AI can’t&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Ownership when code breaks&lt;/li&gt;
  &lt;li&gt;Collaboration to build trust&lt;/li&gt;
  &lt;li&gt;Communication to present technical problems clearly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s what you can’t automate. Those skills worked 10 years ago and will work 10 years from now.&lt;/p&gt;

&lt;p&gt;To help you build the skills to stand out, I wrote &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=quick-lesson-long-debugging-session&quot;&gt;Street-Smart Coding&lt;/a&gt;—The roadmap I wish I had on my journey from junior to senior.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Forget Syntax and Lines of Code. Instead Do This to Stand Out</title>
   <link href="https://canro91.github.io/2026/01/04/CodeCantSpeak/"/>
   <updated>2026-01-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/04/CodeCantSpeak</id>
   <content type="html">&lt;p&gt;Good code won’t save your career.&lt;/p&gt;

&lt;p&gt;For so long, I chased perfectly clean code, thinking better code = better cod-er. That turned me into a clean code cop, looking for infractions around me. &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;It got me fired&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Focusing on syntax alone was &lt;a href=&quot;/2025/10/21/BiggestMistake/&quot;&gt;my biggest mistake as a new coder&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;why-code-isnt-enough&quot;&gt;Why code isn’t enough&lt;/h2&gt;

&lt;p&gt;Two experiences taught me good code isn’t what mattered most.&lt;/p&gt;

&lt;p&gt;At a past job, when the team leader left, &lt;a href=&quot;/2024/12/01/WhoGetsPromoted/&quot;&gt;the one promoted wasn’t the best coder&lt;/a&gt;. It was the one who showed initiative to own the core feature.&lt;/p&gt;

&lt;p&gt;Then during layoffs, I talked to a leader from another team. He was told to sort people into buckets: A, B, and C. Bucket C left first, then B, then A. The criteria wasn’t perfect code. It was whether the leader wanted you on the team. Of course, the ones writing horrible code got into the C crowd first.&lt;/p&gt;

&lt;p&gt;It was always something else besides coding.&lt;/p&gt;

&lt;h2 id=&quot;your-code-cant-speak&quot;&gt;Your code can’t speak.&lt;/h2&gt;

&lt;p&gt;Addy Osmani, a leader at Google, shares why you need more than code to stand out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://addyosmani.com/blog/21-lessons/&quot;&gt;He wrote&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Your code doesn’t advocate for you. People do.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;…Code sits silently in a repository. Your manager mentions you in a meeting, or they don’t. A peer recommends you for a project, or someone else.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Get good at coding to stand out. No doubt!&lt;/p&gt;

&lt;p&gt;But your code won’t speak for you.&lt;/p&gt;

&lt;p&gt;Here’s what will:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Asking the right questions in meetings.&lt;/li&gt;
  &lt;li&gt;Working on something that brings (or saves) money.&lt;/li&gt;
  &lt;li&gt;Sharing your achievements after every project.&lt;/li&gt;
  &lt;li&gt;Being responsible for a feature from end to end.&lt;/li&gt;
  &lt;li&gt;Being the team member everyone wants to work with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People won’t remember your code. They will remember your attitude.&lt;/p&gt;

&lt;p&gt;That’s why I wrote &lt;em&gt;Street-Smart Coding,&lt;/em&gt; the roadmap I wish I had starting out to grow and stand out as a coder.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=forget-syntax-lines-of-code-instead-do-this&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>3 Lessons From Robert Greene&apos;s Mastery to Unlock Your Inner Genius (You Can Be the Next Da Vinci or Einstein)</title>
   <link href="https://canro91.github.io/2026/01/03/Mastery/"/>
   <updated>2026-01-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/03/Mastery</id>
   <content type="html">&lt;p&gt;There’s no secret behind the great masters of history.&lt;/p&gt;

&lt;p&gt;We often think their success comes from a wealthy family, superintelligence, or invisible forces from other planets.&lt;/p&gt;

&lt;p&gt;But in &lt;em&gt;Mastery&lt;/em&gt;, Robert Greene shows there’s a process behind the success of those great minds. A process we can replicate to become a master too.&lt;/p&gt;

&lt;p&gt;Here are my three main lessons from &lt;em&gt;Mastery&lt;/em&gt;:&lt;/p&gt;

&lt;h2 id=&quot;1-mastery-is-like-swimming&quot;&gt;#1. “Mastery is like swimming”&lt;/h2&gt;

&lt;p&gt;We all have a natural inclination.&lt;/p&gt;

&lt;p&gt;Maybe it’s writing, connecting with others, dancing, numbers, or animals. We’re drawn to something when money and a safe path aren’t involved.&lt;/p&gt;

&lt;p&gt;Often, our inclination is clear when we’re kids.&lt;/p&gt;

&lt;p&gt;For Einstein, that was seeing a compass for the first time. The forces moving the needle captivated his whole life.&lt;/p&gt;

&lt;p&gt;To achieve mastery, follow your natural inclination and your strengths.&lt;/p&gt;

&lt;p&gt;Like in swimming, you’ll barely move if you’re going against the current. That’s what happens when you try to master something you’re not suited for.&lt;/p&gt;

&lt;h2 id=&quot;2-discover-your-lifes-task&quot;&gt;#2. Discover your Life’s Task&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Your Life’s Task is to bring [the seed planted at birth] to flower, to express your uniqueness through your work.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some masters know their Life’s Task since childhood. But others only find it after a phase of discovery and exploration.&lt;/p&gt;

&lt;p&gt;Yoky Matsuoka is one of those masters.&lt;/p&gt;

&lt;p&gt;She wasn’t interested in the traditional careers like law or medicine. She was into tennis. Her parents sent her from Japan to the US to pursue tennis, but an injury ended that path.&lt;/p&gt;

&lt;p&gt;Unsure what to study, she chose engineering. Later, her interest in how hands work (from tennis) led her to robotics, and eventually neuroscience to study the brain-hand connection. By combining her passions, she revolutionized the design of robotic hands, modeling them after human hands.&lt;/p&gt;

&lt;p&gt;If you don’t fit in any field or &lt;a href=&quot;/2024/12/16/FindYourPassion/&quot;&gt;have multiple passions&lt;/a&gt;, create yours by exploring and combining other fields.&lt;/p&gt;

&lt;h2 id=&quot;3-the-process-behind-great-masters&quot;&gt;#3. The process behind great masters&lt;/h2&gt;

&lt;p&gt;All masters go through a similar 3-phase process:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Apprentice, Creative/Active, and Mastery.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First, they immerse in a field, learning as much as they can. For Leonardo, it was countless hours of sketching the landscape of his walks around the forest. Then, it was working under Verrocchio, one of the master painters of his time.&lt;/p&gt;

&lt;p&gt;Then, all that immersion clicks and the master ventures into their own creations. That usually means taking distance from mentors and finding their own way.&lt;/p&gt;

&lt;p&gt;Finally, their field becomes second nature, guided by intution. Notes flow on a piano, scenes come alive on canvas. The apprentice becomes a master. And that’s not the result of magic, but immersion, practice, and persistence—A path open to anyone willing to walk it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Programming is dead, naming tools, and better than cheap</title>
   <link href="https://canro91.github.io/2026/01/02/FridayLinks/"/>
   <updated>2026-01-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/02/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; One of my worst career mistakes was going on auto-pilot without a plan. I jumped from job to job until I got bored or fired. But &lt;a href=&quot;https://gregmckeown.com/if-you-dont-design-your-career-someone-else-will/&quot;&gt;if you don’t plan your career, someone else will&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; 2026 is the year to start a side project? Just find a focused time block. Maybe that’s &lt;a href=&quot;https://www.scd31.com/posts/programming-on-the-subway&quot;&gt;coding on the subway&lt;/a&gt; (6min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; A summary of &lt;a href=&quot;https://antirez.com/news/157&quot;&gt;the state of AI at the end of 2025&lt;/a&gt; (4min) by the Redis’ creator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Holiday season is usually for reflection. If you haven’t yet, try these &lt;a href=&quot;https://alearningaday.blog/2025/12/24/10-questions-annual-reflection-2025/&quot;&gt;a 10-question&lt;/a&gt; (4min) or &lt;a href=&quot;https://github.com/kepano/40-questions/blob/master/year.md&quot;&gt;a 40-question prompts&lt;/a&gt; (3min). I followed that last one.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/12/28/AIFomo/&quot;&gt;feeling AI FOMO&lt;/a&gt; (2min) and a &lt;a href=&quot;/2025/12/29/Decluttering/&quot;&gt;30-action digital decluttering plan&lt;/a&gt; (4min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… my new book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding in 2026!&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>2025: My Year in Review</title>
   <link href="https://canro91.github.io/2026/01/01/BestOf2025/"/>
   <updated>2026-01-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2026/01/01/BestOf2025</id>
   <content type="html">&lt;p&gt;I’ll remember 2025 as the year I became a writer—and the year I lost my mom.&lt;/p&gt;

&lt;h2 id=&quot;the-saddest-moment&quot;&gt;The saddest moment&lt;/h2&gt;

&lt;p&gt;After five years of fighting a chronic disease, she left.&lt;/p&gt;

&lt;p&gt;It was the saddest, loneliest moment of my life. It led me to &lt;a href=&quot;/2025/08/28/TheSaddestPost/&quot;&gt;my toughest post ever&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To honor her memory, my sister and I wrote a book: &lt;a href=&quot;https://www.amazon.com/dp/B0GCMVXL7S&quot;&gt;Nuestros Recuerdos&lt;/a&gt; (Our Memories in Spanish).&lt;/p&gt;

&lt;p&gt;We compiled my journal entries and her poetry. A friend drew some illustrations. And we found some pictures of her when she was young.&lt;/p&gt;

&lt;p&gt;That was our therapy and way to keep her alive in our memory.&lt;/p&gt;

&lt;p&gt;2025 was a year of achievements and melancholy, winning and losing, endings and beginnings.&lt;/p&gt;

&lt;h2 id=&quot;some-victory-dance-moments&quot;&gt;Some victory dance moments&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. My first viral post.&lt;/strong&gt; One post about &lt;a href=&quot;/2024/11/30/TestingPrivateMethods/&quot;&gt;testing private methods&lt;/a&gt; got syndicated or reshared. BOOM! A traffic spike and some ebook sales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. My first interview.&lt;/strong&gt; Well, it wasn’t exactly an interview.&lt;/p&gt;

&lt;p&gt;The LAX community invited me to &lt;a href=&quot;/2025/09/11/ALXSept2025/&quot;&gt;a QA session with their coding students&lt;/a&gt;. They’re building the next generation of coders and leaders in Africa.&lt;/p&gt;

&lt;p&gt;Their community manager found one of my posts and reached out. Another victory for writing.&lt;/p&gt;

&lt;p&gt;That interview was the perfect excuse to ask for feedback on my communication skills. A coach taught me &lt;a href=&quot;/2025/09/13/Answering/&quot;&gt;this 4-step framework&lt;/a&gt; for interviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. My first book&lt;/strong&gt;, &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It took about 4 months, from idea to typing the last word. Writing taught me plenty, but promoting taught me more.&lt;/p&gt;

&lt;p&gt;I adopted &lt;a href=&quot;/2025/10/11/ContentThatSells/&quot;&gt;a simple marketing strategy&lt;/a&gt; and passed &lt;a href=&quot;/2025/10/12/1DollarTest/&quot;&gt;the $1 test&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I translated it to Spanish and published it &lt;a href=&quot;https://www.amazon.com/dp/B0G5XCWKR9&quot;&gt;on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And with a book came &lt;a href=&quot;/2025/11/02/Haters/&quot;&gt;my first hater&lt;/a&gt;. Someone called me “b1tch” for promoting my book and left a sarcastic comment elsewhere.&lt;/p&gt;

&lt;p&gt;The book was a real challenge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. My first appearance on Hacker News.&lt;/strong&gt; Yes, &lt;a href=&quot;https://hn.algolia.com/?q=canro91&quot;&gt;my blog got syndicated on HN&lt;/a&gt;. I didn’t make it to the first page. But hey, it’s progress.&lt;/p&gt;

&lt;h2 id=&quot;my-most-read-posts&quot;&gt;My most read posts&lt;/h2&gt;

&lt;p&gt;In 2025, I wrote every single day, hitting the 400-post mark.&lt;/p&gt;

&lt;p&gt;Among those daily posts, here are the top 10 posts by reads:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/03/19/ChangedMyMind/&quot;&gt;9 Subjects I’ve Changed My Mind About as a Software Engineer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Six Proven Principles to Learn Any Skill Faster (Without Spending 10,000 Hours)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/25/LessonsFromMentor/&quot;&gt;5 Lessons from My Team’s Architect That Helped Me Become a Senior Developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;Don’t Write the Next Atomic Habits. Write Mini-Books&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/17/HarshTruths/&quot;&gt;12 Hard Truths About Coding I Learned the Hard Way After 10 Years&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/07/03/LifeChangingIdeas/&quot;&gt;7 Surprisingly Simple Ideas That Changed My Life (And Could Change Yours Too)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;10 Ways to Stand Out at Work—Other than Work Hard&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/28/Frustrations/&quot;&gt;What Frustrates Me the Most as a C#/.NET Developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/06/22/SimplerNoteTaking/&quot;&gt;I’ve Replaced My Second Brain With a Simpler Method&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/19/YouAreNotAProgrammerUntil/&quot;&gt;20+ Signs You’re a Real Programmer (Using 2 Monitors Isn’t One)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For 2026, apart from working on my health daily (simple idea #2 from the post #7), I’m making simplicity my intention. And I started &lt;a href=&quot;/2025/12/29/Decluttering/&quot;&gt;decluttering my digital life&lt;/a&gt;. That intention will impact my content system too.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding in 2025!&lt;/p&gt;

&lt;p&gt;Don’t miss my &lt;a href=&quot;/2025/01/01/BestOf2024/&quot;&gt;best of 2024&lt;/a&gt;, &lt;a href=&quot;/2024/01/08/BestOf2023/&quot;&gt;2023&lt;/a&gt;, &lt;a href=&quot;/2023/01/09/BestOf2022/&quot;&gt;2022&lt;/a&gt;, and &lt;a href=&quot;/2022/01/10/BestOf2021/&quot;&gt;2021&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Books I Read (and Books I Didn&apos;t Finish) in 2025</title>
   <link href="https://canro91.github.io/2025/12/31/Books/"/>
   <updated>2025-12-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/31/Books</id>
   <content type="html">&lt;p&gt;YouTube changed how I read books.&lt;/p&gt;

&lt;p&gt;One day, the YouTube algorithm showed me one video on &lt;a href=&quot;/2025/04/28/OneBookAWeek/&quot;&gt;reading one book per week&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That video made me read two books at once. Now I keep a book on my dinner table and another on the couch. Those are my reading spots after meals.&lt;/p&gt;

&lt;p&gt;And binge-watching Ryan Holiday’s YouTube channel made me &lt;a href=&quot;/2025/09/08/StoicBookReading/&quot;&gt;change my reading habits&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I went back to reading on paper and writing in margins. That used to feel like a capital sin.&lt;/p&gt;

&lt;p&gt;With those two strategies, here are the books I read and one takeaway from each:&lt;/p&gt;

&lt;h2 id=&quot;books-i-finished&quot;&gt;Books I finished&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Skip the Line&lt;/a&gt; by James Altucher: Forget about the 10,000 hours to become an expert. Instead of accumulating hours, run 10,000 experiments: quick actions that teach you something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt; by Austen Kleon: Originality is overrated. Find who to copy. Then find what to copy. My favorite line: &lt;em&gt;“Hands first, then computer.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; &lt;a href=&quot;/2025/12/27/MiniBookModel/&quot;&gt;Mini Book Model&lt;/a&gt; by Chris Stanley: In 2025, I redefined what a book is.&lt;/p&gt;

&lt;p&gt;These days, the real challenge is to make people finish books. Social media has ruined our attention spans.&lt;/p&gt;

&lt;p&gt;The solution? Write shorter books.&lt;/p&gt;

&lt;p&gt;James Altucher planted a seed with &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;his “10-paper book” challenge&lt;/a&gt;. Building on that idea, Chris’ book gave me frameworks to title, outline, and write “mini books.”&lt;/p&gt;

&lt;p&gt;I’m following the mini book principles to write my next coding books.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; &lt;a href=&quot;/2025/01/20/WritingToLearn/&quot;&gt;Writing to Think&lt;/a&gt; by William Zinsser. My favorite line: &lt;em&gt;“Writing is learned mainly by imitation.”&lt;/em&gt; That felt like permission to explore and develop my own voice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5.&lt;/strong&gt; &lt;a href=&quot;/2025/08/21/21Lessons/&quot;&gt;21 Lessons for the 21st Century&lt;/a&gt; by Yuval Noah Harari. Making predictions is hard, especially about the future. With the uncertainty of what AI could bring, the sole skill to master is the ability to learn and adapt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6.&lt;/strong&gt; &lt;a href=&quot;/2025/02/14/WritingForDevelopers/&quot;&gt;Writing for Developers&lt;/a&gt; by Piotr Sarna and Cynthia Dunlop. My favorite line? &lt;a href=&quot;/2025/02/25/YouAreNotWritingEnough/&quot;&gt;You’re not writing enough&lt;/a&gt;. The book shared it for engineering blogging, but the advice applies everywhere. Write more!&lt;/p&gt;

&lt;h2 id=&quot;books-i-didnt-finish-but-im-still-reading&quot;&gt;Books I didn’t finish but I’m still reading&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#7. Mastery by Robert Greene.&lt;/strong&gt; Some masters knew their Life Task as children. Others discovered it through experimentation and exploration, at the intersection of fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Glucose Revolution by Jessie Inchauspé.&lt;/strong&gt; My sister picked up this one first. I started to apply some concepts to keep my glucose spikes under control. That’s my secret productivity hack to avoid the afternoon crash after lunch.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Don&apos;t Quit Your Day Job—Create in Two Hours a Day Instead</title>
   <link href="https://canro91.github.io/2025/12/30/TwoHours/"/>
   <updated>2025-12-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/30/TwoHours</id>
   <content type="html">&lt;p&gt;“If I had more hours, I’d build more things.”&lt;/p&gt;

&lt;p&gt;Anyone else feel that? Or that’s just me?&lt;/p&gt;

&lt;p&gt;I’ve wanted to quit my day job more times than I can count.&lt;/p&gt;

&lt;p&gt;I stopped thinking about quitting when I discovered the idea of a daily creative block.&lt;/p&gt;

&lt;h2 id=&quot;two-hours-are-enough-to-create-evergreen-content&quot;&gt;Two hours are enough to create evergreen content.&lt;/h2&gt;

&lt;p&gt;Mark Thompson works for 2 hours.&lt;/p&gt;

&lt;p&gt;He’s a veteran marketer who now creates for a living. He replies, posts, and runs a community. All in about 2 hours every day.&lt;/p&gt;

&lt;p&gt;About working for just 2 hours, &lt;a href=&quot;https://seriousmarketersonly.medium.com/two-hours-of-work-evergreen-income-00e7a50f6127&quot;&gt;he wrote&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Two hours of focus work today can create a lifetime of income tomorrow.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was &lt;a href=&quot;/2025/11/15/LinesThatMadeMeThink/&quot;&gt;one of the lines that made me think recently&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;a-professional-writer-doesnt-write-for-that-long&quot;&gt;A professional writer doesn’t write for that long.&lt;/h2&gt;

&lt;p&gt;Mark isn’t the only one with that routine.&lt;/p&gt;

&lt;p&gt;Steven Pressfield, author of &lt;em&gt;War of Art&lt;/em&gt;, follows the same practice.&lt;/p&gt;

&lt;p&gt;In an interview in Huberman Labs, he shared that he only writes for…guess how long? Two hours. After that, it’s time for interviews, marketing, and promotion.&lt;/p&gt;

&lt;p&gt;If pros write only for two hours, we don’t need to quit. We need habit and discipline first.&lt;/p&gt;

&lt;p&gt;Austen Kleon said it in &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt;, &lt;em&gt;keep your day job&lt;/em&gt;. I stole his idea of checking a box every time I work on my projects. That’s my plan for 2026 and the years to come.&lt;/p&gt;

&lt;p&gt;You just need two hours. Start today. The time to quit will come later.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My 30-Action Digital Decluttering Plan For a Fresh Start (Useful Every Year)</title>
   <link href="https://canro91.github.io/2025/12/29/Decluttering/"/>
   <updated>2025-12-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/29/Decluttering</id>
   <content type="html">&lt;p&gt;I’ve tried resolutions. But they didn’t work.&lt;/p&gt;

&lt;p&gt;On New Year’s Eve, excitement fills the air: holidays, dinner, and presents. But that excitement often becomes a to-do list we never complete.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;Instead of resolutions and to-dos&lt;/a&gt;, I prefer to go with an intention.&lt;/p&gt;

&lt;p&gt;Last year, my intention was &lt;em&gt;“health.”&lt;/em&gt; I did something for my body, mind, and spirit every day. &lt;a href=&quot;/2025/07/03/LifeChangingIdeas/&quot;&gt;One simple idea that changed my life&lt;/a&gt;. A 15-min workout session, writing, and a moment of silence kept me sane.&lt;/p&gt;

&lt;p&gt;For this year, I’m making &lt;em&gt;“simplicity”&lt;/em&gt; my new intention. And to start, I’m decluttering my digital life. Here’s my decluttering plan:&lt;/p&gt;

&lt;h2 id=&quot;computer&quot;&gt;Computer&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Before messing with anything, &lt;strong&gt;run a backup&lt;/strong&gt;. It doesn’t have to be complicated. Plug in a USB drive and copy what you want to keep. I use &lt;a href=&quot;https://freefilesync.org/&quot;&gt;FreeFileSync&lt;/a&gt; to automate backups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Keep the Desktop clean.&lt;/strong&gt; All those temporary files looking for a folder: Gone!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Clean the Downloads folder.&lt;/strong&gt; Remove all those freebies you never read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Archive unused files from Documents.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Uninstall unused software.&lt;/strong&gt; If you haven’t used it in the last 6 months, it’s out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Delete old backups.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Archive notes you no longer need or have already used.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Remove screenshots you don’t need anymore.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Clean your RSS reader.&lt;/strong&gt; Unfollow dormant blogs or sites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Clean your password manager.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#11. Discard one-time coding projects or folders.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#12.&lt;/strong&gt; Alternatively, &lt;strong&gt;run BleachBit, CCleaner, or similar apps.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For each browser…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#13. Remove bookmarks.&lt;/strong&gt; I often use bookmarks as a cheap “Read for Later” app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#14. Clear browser history, cookies, and saved data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#15. Remove or change pinned tabs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#16. Uninstall extensions you don’t need.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;online&quot;&gt;Online&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#17. Turn off email notifications.&lt;/strong&gt; I don’t need an email for every like, comment, or repost. Leave email for 2-factor authentication or security notifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#18. Unfollow people from social media accounts.&lt;/strong&gt; If they haven’t published anything in the last 6 months, out. If their message doesn’t resonate anymore, out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#19. Clear saved content from social media apps&lt;/strong&gt;, like Medium, LinkedIn, or dev.to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#20. Delete old email notifications.&lt;/strong&gt; My bank sends me an email every time I buy something. If I still need them, I can generate a report from the bank’s website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#21. Unsubscribe from newsletters.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#22. Archive reference emails.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#23. Clean Canva projects.&lt;/strong&gt; Often I create multiple versions of the same design, but only the latest matters.&lt;/p&gt;

&lt;h2 id=&quot;phone&quot;&gt;Phone&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#24. Clean the homepage.&lt;/strong&gt; Leave no apps or icons there. Group related apps inside folders on the second page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#25. Remove social media and unused apps.&lt;/strong&gt; Again, run the 6-month test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#26. Remove memes and large files from WhatsApp groups.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#27. Remove “bad” photos.&lt;/strong&gt; People in funny poses, with closed eyes: gone!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#28. Remove to-watch or to-read notes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#29. Clean your browsers’ history.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#30.&lt;/strong&gt; If you use YouTube, &lt;strong&gt;clean your search history and change Google’s Ads ID.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#31. Clear Cloud storage.&lt;/strong&gt; I keep extra copies when sharing files between computers.&lt;/p&gt;

&lt;p&gt;I thought about making a checklist on &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;Gumroad&lt;/a&gt;, but that would just add clutter to your downloads. Instead, bookmark this post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dear Coder: Open This If You&apos;re Feeling AI FOMO</title>
   <link href="https://canro91.github.io/2025/12/28/AIFomo/"/>
   <updated>2025-12-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/28/AIFomo</id>
   <content type="html">&lt;p&gt;It’s all over the headlines.&lt;/p&gt;

&lt;p&gt;Every so often, someone reveals a model X% faster. A tech CEO makes bold claims about the future of coding. The stock market reacts with euphoria. Prices go up.&lt;/p&gt;

&lt;p&gt;AI has also brought new buzzwords. Agents, prompts, workflows, integrations…&lt;/p&gt;

&lt;h2 id=&quot;pick-yourbattles&quot;&gt;Pick your battles&lt;/h2&gt;

&lt;p&gt;FOMO is real.&lt;/p&gt;

&lt;p&gt;Almost every week, I find tutorials about “MCP.” Whatever that means. I skip most of them. &lt;a href=&quot;/2025/06/03/Hype/&quot;&gt;I’ve chosen my battles&lt;/a&gt; to stay sane in the hype cycle.&lt;/p&gt;

&lt;p&gt;I’m still figuring out &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;how to adopt AI without losing my skills&lt;/a&gt; and &lt;a href=&quot;/2025/12/01/TooMuchAI/&quot;&gt;identifying the problems it brings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s keeping me busy enough.&lt;/p&gt;

&lt;h2 id=&quot;some-things-haventchanged&quot;&gt;Some things haven’t changed&lt;/h2&gt;

&lt;p&gt;If you’re also experiencing FOMO:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t worry. Fundamentals matter more than ever.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Plenty of codebases still suffer from N+1 problems, missing pagination, and SQL injection. Most were written before the AI hype. Now imagine the chaos inside vibe-coded codebases.&lt;/p&gt;

&lt;p&gt;There’s still work ahead before (or if) &lt;a href=&quot;/2025/12/21/Offerings/&quot;&gt;AI shines at coding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/21/AIAdaptation/&quot;&gt;Don’t forget about AI&lt;/a&gt;. But build real skills first. Then leverage AI. In that order.&lt;/p&gt;

&lt;p&gt;To help you build lasting skills beyond the hype, I wrote &lt;em&gt;Street-Smart Coding.&lt;/em&gt; The roadmap I wish I had starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=open-youre-feeling-ai-fomo&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Lessons From the Mini Book Model to Write Shorter, More Impactful Books</title>
   <link href="https://canro91.github.io/2025/12/27/MiniBookModel/"/>
   <updated>2025-12-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/27/MiniBookModel</id>
   <content type="html">&lt;p&gt;Forget 30,000 words. Forget publishers.&lt;/p&gt;

&lt;p&gt;For so long, writing a book felt distant: original ideas, months of research, and plenty of rejections. Then I discovered self-publishing had redefined &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;what a book could be&lt;/a&gt;. And finally, Chris Stanley debunked those objections with &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;the concept of a mini book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After attending one of Chris’ workshops last year, I devoured his book, &lt;em&gt;Mini Book Model&lt;/em&gt;. Here are 7 lessons I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Start with a problem and your point of view (POV).&lt;/strong&gt; Instead of starting with a blank page, identify one problem your reader faces and how you solved it.&lt;/p&gt;

&lt;p&gt;Since you’re writing about your solution, you don’t need months of research. Often, research is procrastination in disguise.&lt;/p&gt;

&lt;p&gt;Apart from a problem and solution, after &lt;a href=&quot;/2025/11/16/LaunchingABook/&quot;&gt;launching Street-Smart Coding&lt;/a&gt;, I learned to write a book description and &lt;a href=&quot;/2025/11/09/OneLine/&quot;&gt;a one-line summary&lt;/a&gt; before the content. That forces you to clarify your message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Resist the urge of adding more fluff.&lt;/strong&gt; Unlike traditional non-fiction books, the point of a mini book is to present a message as fast and concisely as possible.&lt;/p&gt;

&lt;p&gt;As a guideline:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If you reach 5,000 words, you have enough.&lt;/li&gt;
  &lt;li&gt;If you pass 15,000 words, stop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A target word count isn’t the goal, but to fulfill your book promise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Use the W’s outline to present new concepts.&lt;/strong&gt; With the W’s outline, use one chapter to answer what/why/who/when/where/how. It’s the perfect outline to introduce a new concept. Tweak to emphasize the “how,” if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. If you have a process or framework, write a mini book about it.&lt;/strong&gt; Make each step of your process a chapter of your mini book. Then, go deeper on a step with another mini book. And plug future books into earlier ones.&lt;/p&gt;

&lt;p&gt;That’s what Chris did with the whole &lt;em&gt;Mini Book&lt;/em&gt; series. The first book, &lt;em&gt;Mini Book Model&lt;/em&gt; introduces what a mini book is and how to write one. Then he wrote another mini book about each step in the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Use 3 points per chapter and 3 subpoints per point.&lt;/strong&gt; The goal is to keep your mini book focused and on point. But feel free to use the points you need to deliver your message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Introduce a checkpoint in the first half.&lt;/strong&gt; This isn’t a lesson Chris taught, but one he practiced in the book itself. Instead of waiting until the end to ask for a review, he added a call-to-action after the first chapters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. To edit your mini book, read it out loud while recording it.&lt;/strong&gt; It helps you catch typos and speed up your audiobook launch. Win-win!&lt;/p&gt;

&lt;p&gt;Mini books give you permission to write and launch without gatekeepers. No more 30,000 words. That’s liberating. What are you waiting to join the mini book movement?&lt;/p&gt;

&lt;p&gt;I’ve embraced mini books in the coding space, and you can &lt;a href=&quot;https://imcsarag.gumroad.com/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=lessons-mini-book-model&quot;&gt;find my mini books here&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Programming is dead, naming tools, and better than cheap</title>
   <link href="https://canro91.github.io/2025/12/26/FridayLinks/"/>
   <updated>2025-12-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/26/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; “AI replacing coders” and “programming being dead” have been all over the news. But that’s not true. &lt;a href=&quot;https://terriblesoftware.org/2025/12/11/ai-can-write-your-code-it-cant-do-your-job/&quot;&gt;AI can “write” code, but it can’t do our job&lt;/a&gt; (5min). Coding has never been the bottleneck or the most valuable skill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Naming is hard. Especially when we’re naming packages or tools. We’ve gone from descriptive to funny, unrelated names (Viper, Cobra, Melody). &lt;a href=&quot;https://larr.net/p/namings.html&quot;&gt;We’ve lost the plot of naming tools&lt;/a&gt; (9min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; If we want to stand out among the crowd, we have to &lt;a href=&quot;https://seths.blog/2025/12/better-than-the-cheap-alternative/&quot;&gt;be better than the cheap alternative&lt;/a&gt; (1min): frozen pizza, smartphone camera, or AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Looking for an interesting read this holiday season? Well, check out &lt;a href=&quot;https://readsomethingwonderful.com/&quot;&gt;Read Something Wonderful&lt;/a&gt;. Just a few clicks took me to the 1,000 true fans essays, one of the most popular ones on the Internet.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/12/21/Offerings/&quot;&gt;What coders could offer instead of LOCs if AI takes over&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/12/22/AIRuiningDegrees/&quot;&gt;vibecoding ruining CS degrees&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… my new book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Answering the 40-Questions Reflection Prompt</title>
   <link href="https://canro91.github.io/2025/12/25/40Questions/"/>
   <updated>2025-12-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/25/40Questions</id>
   <content type="html">&lt;p&gt;I’m running another internet challenge.&lt;/p&gt;

&lt;p&gt;Well, I don’t know if this was intended as a “challenge” or more like a year reflection. But I found this idea on &lt;a href=&quot;https://blog.avas.space/40-questions-2025/&quot;&gt;Ava’s blog&lt;/a&gt;. Ava, if you’re reading this, please keep sharing those challenge ideas, I like them.&lt;/p&gt;

&lt;p&gt;Unlike most of my posts, I’m not writing them on &lt;a href=&quot;https://www.squibler.io/dangerous-writing-prompt-app&quot;&gt;The Most Dangerous Writing App&lt;/a&gt; or fully editing them. If this were a video or a song, I’d be making a single take.&lt;/p&gt;

&lt;h2 id=&quot;1-what-did-you-do-this-year-that-youd-never-done-before&quot;&gt;#1. What did you do this year that you’d never done before?&lt;/h2&gt;

&lt;p&gt;A book! I wrote my first official book, &lt;em&gt;&lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had written two small books before. But I wasn’t confident enough in my writing that I didn’t call them books, but “ebooks.”&lt;/p&gt;

&lt;p&gt;After finding about &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;mini books&lt;/a&gt;, I changed my mind about &lt;a href=&quot;/2025/07/22/Books/&quot;&gt;what a book is&lt;/a&gt; and realized I could write one. So I did it.&lt;/p&gt;

&lt;h2 id=&quot;2-did-you-keep-your-new-years-resolutions&quot;&gt;#2. Did you keep your new year’s resolutions?&lt;/h2&gt;

&lt;p&gt;I don’t do resolutions anymore.&lt;/p&gt;

&lt;p&gt;I used to make a list of resolutions: learning a new language, traveling, doing this or that. But I ended up doing different things from the list.&lt;/p&gt;

&lt;p&gt;After resolutions, I tried with “themes,” like Yoga March or Side Projects November. Dunno, they didn’t work either.&lt;/p&gt;

&lt;p&gt;My only resolution, let’s say, is to &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;do my daily practice&lt;/a&gt;: doing something for my body, mind, and spirit every day. That’s the only goal/plan I’m carrying over 2026.&lt;/p&gt;

&lt;h2 id=&quot;3-did-anyone-close-to-you-give-birth&quot;&gt;#3. Did anyone close to you give birth?&lt;/h2&gt;

&lt;p&gt;Yes! One of my best friends after trying for a while had her first baby. Hoooray!&lt;/p&gt;

&lt;h2 id=&quot;4-did-anyone-close-to-you-die&quot;&gt;#4. Did anyone close to you die?&lt;/h2&gt;

&lt;p&gt;Yes. I lost my mom.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/11/29/Fighting/&quot;&gt;She fought with a chronic disease for over 5 years&lt;/a&gt;. And being a writer, I couldn’t avoid writing about her. That’s &lt;a href=&quot;/2025/08/28/TheSaddestPost/&quot;&gt;the saddest post I’ve ever had to write&lt;/a&gt;. And my sister and I are about to release a book about our mom.&lt;/p&gt;

&lt;h2 id=&quot;5-what-citiesstatescountries-did-you-visit&quot;&gt;#5. What cities/states/countries did you visit?&lt;/h2&gt;

&lt;p&gt;I visited my hometown a couple of times. In fact, I’m writing this in my grandma’s place, where I grew up.&lt;/p&gt;

&lt;h2 id=&quot;6-what-would-you-like-to-have-next-year-that-you-lacked-this-year&quot;&gt;#6. What would you like to have next year that you lacked this year?&lt;/h2&gt;

&lt;p&gt;Health…and simplicity.&lt;/p&gt;

&lt;p&gt;I’m not a sick person. But last year, I had to visit an ER with kidney stones and see a doctor a couple of times. I’m focusing on healthy habits next year: eating the right food in the right amount.&lt;/p&gt;

&lt;h2 id=&quot;7-what-dates-from-this-year-will-remain-etched-upon-your-memory-and-why&quot;&gt;#7. What date(s) from this year will remain etched upon your memory, and why?&lt;/h2&gt;

&lt;p&gt;The date my mom left.&lt;/p&gt;

&lt;p&gt;To honor my mom’s memory, I turned that date into my launch day for books and other projects. It’s my way to remember her and dedicate my work to her.&lt;/p&gt;

&lt;h2 id=&quot;8-what-was-your-biggest-achievement-of-the-year&quot;&gt;#8. What was your biggest achievement of the year?&lt;/h2&gt;

&lt;p&gt;The book! I’m proud of it.&lt;/p&gt;

&lt;h2 id=&quot;9-what-was-your-biggest-failure&quot;&gt;#9. What was your biggest failure?&lt;/h2&gt;

&lt;p&gt;I wouldn’t say failure, but that would be landing my first paying ghostwriting client.&lt;/p&gt;

&lt;p&gt;After doubling down on my writing last year, becoming a ghostwriter was the natural next step for me. I bought a ghostwriting program and even changed my LinkedIn bio to include “ghostwriter.” I focused on releasing my book and set the ghostwriting gig aside.&lt;/p&gt;

&lt;h2 id=&quot;10-what-other-hardships-did-you-face&quot;&gt;#10. What other hardships did you face?&lt;/h2&gt;

&lt;p&gt;I visited ERs and ICUs with my mom a couple of times.&lt;/p&gt;

&lt;p&gt;It was hard. Waiting in rooms, waiting for blood donors, waiting for diagnosis, waiting for surgery paperwork…Traveling across town to spend the day with her while she was in a hospital. Days fulls of uncertainty.&lt;/p&gt;

&lt;p&gt;I don’t want to visit a hospital in ages.&lt;/p&gt;

&lt;h2 id=&quot;11-did-you-suffer-illness-or-injury&quot;&gt;#11. Did you suffer illness or injury?&lt;/h2&gt;

&lt;p&gt;Yes. Kidney stones. OMG! It’s painful. Imagine a Heavyweight boxer punching you next to your bladder every second. That’s how it feels…until the stone is out.&lt;/p&gt;

&lt;h2 id=&quot;12-what-was-the-best-thing-you-bought&quot;&gt;#12. What was the best thing you bought?&lt;/h2&gt;

&lt;p&gt;That’s probably a comfty pair of shoes…and notepads to run &lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;my 10-idea lists&lt;/a&gt;. That’s how I wrote my book and countless of pieces of content.&lt;/p&gt;

&lt;h2 id=&quot;13-whose-behavior-merited-celebration&quot;&gt;#13. Whose behavior merited celebration?&lt;/h2&gt;

&lt;p&gt;This year, I learned the value of real friendship.&lt;/p&gt;

&lt;p&gt;In my saddest moment, two of my friends were with my sister and me. And when another friend found out my mom, he rushed from his city to stay with us for a day.&lt;/p&gt;

&lt;h2 id=&quot;14-whose-behavior-made-you-appalled&quot;&gt;#14. Whose behavior made you appalled?&lt;/h2&gt;

&lt;p&gt;Dunno if appalled, but…Online haters.&lt;/p&gt;

&lt;p&gt;With more eye balls on my content, I got &lt;a href=&quot;/2025/11/02/Haters/&quot;&gt;my first hater&lt;/a&gt;, someone calling me “b1tch” for promoting my work. And it seems someone somewhere will always get offended online. The Internet, I guess.&lt;/p&gt;

&lt;h2 id=&quot;15-where-did-most-of-your-money-go&quot;&gt;#15. Where did most of your money go?&lt;/h2&gt;

&lt;p&gt;That’s grocery, bills, medical care, and books.&lt;/p&gt;

&lt;h2 id=&quot;16-what-did-you-get-really-really-really-excited-about&quot;&gt;#16. What did you get really, really, really excited about?&lt;/h2&gt;

&lt;p&gt;Seeing the first preorder for my book.&lt;/p&gt;

&lt;p&gt;When I saw the email notification, I was like “OMG! OMG! Yes! Yes!” A true happy dance moment.&lt;/p&gt;

&lt;p&gt;And months later, I had another happy dance moment when someone called me the “Street-Smart guy” in a comment under one of my posts. That moment I knew my marketing was working.&lt;/p&gt;

&lt;h2 id=&quot;17-what-song-will-always-remind-you-of-this-year&quot;&gt;#17. What song will always remind you of this year?&lt;/h2&gt;

&lt;p&gt;There’s one.&lt;/p&gt;

&lt;p&gt;I can’t remember its title. But it says something like “for a brief moment, there was a future between us.” In Spanish&lt;/p&gt;

&lt;p&gt;Of course, that line goes about someone heartbroken in a romantic relationship. But that’s how my sister and I felt about our mom. Listening to that song is part of our ritual to honor our mom every month.&lt;/p&gt;

&lt;h2 id=&quot;18-compared-to-this-time-last-year-are-you-happier-or-sadder-richer-or-poorer-healthier-or-unhealthier&quot;&gt;#18. Compared to this time last year, are you: happier or sadder? Richer or poorer? Healthier or unhealthier?&lt;/h2&gt;

&lt;p&gt;Happier, richer, and healthier.&lt;/p&gt;

&lt;p&gt;Well, it’s complicated I feel happier and melancholic at the same time. I’ve had good achievements and I wish my mom were here to see them.&lt;/p&gt;

&lt;h2 id=&quot;19-what-do-you-wish-youd-done-more-of&quot;&gt;#19. What do you wish you’d done more of?&lt;/h2&gt;

&lt;p&gt;Being present. I’ve been &lt;a href=&quot;/2024/12/19/TimeTravel/&quot;&gt;a time traveler&lt;/a&gt;. That has taught me our mind is the battle field with all the thoughts that come and go.&lt;/p&gt;

&lt;h2 id=&quot;20-what-do-you-wish-youd-done-less-of&quot;&gt;#20. What do you wish you’d done less of?&lt;/h2&gt;

&lt;p&gt;Scrolling, commenting, and social media.&lt;/p&gt;

&lt;p&gt;As part of growing as ghostwriter, I found “grow your own account” and “be your first success case.” That made me spend quite some time engaging and commenting under LinkedIn posts to grow my presence. I found interesting people and Internet friends tho.&lt;/p&gt;

&lt;p&gt;But I wish I had done social media less.&lt;/p&gt;

&lt;h2 id=&quot;21-how-are-you-spending-the-holidays&quot;&gt;#21. How are you spending the holidays?&lt;/h2&gt;

&lt;p&gt;I took some time off from freelancing. I packed a couple of books with me to visit my uncle and aunt. I’m only writing on my blog and cross-posting to other platforms.&lt;/p&gt;

&lt;p&gt;I’m not fully idle, I decided to pick a light project: &lt;a href=&quot;/2025/12/23/SignOfGrowth/&quot;&gt;I’m revamping an old book&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;22-did-you-fall-in-love-this-year&quot;&gt;#22. Did you fall in love this year?&lt;/h2&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;h2 id=&quot;23-do-you-hate-anyone-now-that-you-didnt-hate-this-time-last-year&quot;&gt;#23. Do you hate anyone now that you didn’t hate this time last year?&lt;/h2&gt;

&lt;p&gt;Noooo. Hate is a strong word.&lt;/p&gt;

&lt;h2 id=&quot;24-what-was-your-favorite-show&quot;&gt;#24. What was your favorite show?&lt;/h2&gt;

&lt;p&gt;#1 by far, House M.D. But I enjoyed Boots, The Recruit, The Diplomat, The Agency. Lots of spy shows, I know.&lt;/p&gt;

&lt;p&gt;In fact, I like House M.D. so much that I wrote a couple of posts about it, &lt;a href=&quot;/2025/05/18/HouseMD/&quot;&gt;this one&lt;/a&gt; and &lt;a href=&quot;/2025/10/27/ProblemSolving/&quot;&gt;this one&lt;/a&gt;, and included a whole chapter about it on &lt;em&gt;Street-Smart Coding.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;25-what-was-the-best-book-you-read&quot;&gt;#25. What was the best book you read?&lt;/h2&gt;

&lt;p&gt;I’m trying to remember the books I read…&lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Skip the Line&lt;/a&gt;, &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt;, and I’m about to finish &lt;em&gt;The Post Office&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;26-what-was-your-greatest-musical-discovery-of-the-year&quot;&gt;#26. What was your greatest musical discovery of the year?&lt;/h2&gt;

&lt;p&gt;I didn’t have any. I enjoy listening to the same songs over and over.&lt;/p&gt;

&lt;h2 id=&quot;27-what-was-your-favorite-film&quot;&gt;#27. What was your favorite film?&lt;/h2&gt;

&lt;p&gt;Not favorite, but I recently enjoyed &lt;a href=&quot;https://www.imdb.com/title/tt27679443/&quot;&gt;Reading Lolita in Tehran&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;28-what-was-your-favorite-meal&quot;&gt;#28. What was your favorite meal?&lt;/h2&gt;

&lt;p&gt;There’s a salad with cucumber, tomato, mint, parsley, and lemon that my sister makes. I love it. I could eat it every day. It’s a Middle-East inspired recipe.&lt;/p&gt;

&lt;h2 id=&quot;29-what-did-you-want-and-get&quot;&gt;#29. What did you want and get?&lt;/h2&gt;

&lt;p&gt;Writing a book.&lt;/p&gt;

&lt;h2 id=&quot;30-what-did-you-want-and-not-get&quot;&gt;#30. What did you want and not get?&lt;/h2&gt;

&lt;p&gt;My mom back home after that last visit to a hospital.&lt;/p&gt;

&lt;h2 id=&quot;31-what-did-you-do-on-your-birthday&quot;&gt;#31. What did you do on your birthday?&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;After picking up my phone to see what photos I took that day…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wore a Cartoon Network themed T-shirt my mom and sister bought me. Little did I know my mom was really in pain when she went out to buy it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I got sentimental writing that last line…&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;32-what-one-thing-would-have-made-your-year-immeasurably-more-satisfying&quot;&gt;#32. What one thing would have made your year immeasurably more satisfying?&lt;/h2&gt;

&lt;p&gt;Seeing mom back home…&lt;/p&gt;

&lt;h2 id=&quot;33-how-would-you-describe-your-personal-fashion-this-year&quot;&gt;#33. How would you describe your personal fashion this year?&lt;/h2&gt;

&lt;p&gt;Just like past years: blue jeans and black t-shirts.&lt;/p&gt;

&lt;h2 id=&quot;34-what-kept-you-sane&quot;&gt;#34. What kept you sane?&lt;/h2&gt;

&lt;p&gt;Doing a 10-min full body workout, running, and writing every day.&lt;/p&gt;

&lt;h2 id=&quot;35-which-celebritypublic-figure-did-you-admire-the-most&quot;&gt;#35. Which celebrity/public figure did you admire the most?&lt;/h2&gt;

&lt;p&gt;#1. &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, his career progression and writing have been really inspiring to me.&lt;/p&gt;

&lt;p&gt;#2. &lt;a href=&quot;/2025/09/08/StoicBookReading/&quot;&gt;Ryan Holiday&lt;/a&gt;. Not that I want to be like him, but I want to make a living reading and writing books too.&lt;/p&gt;

&lt;h2 id=&quot;36-what-political-issue-stirred-you-the-most&quot;&gt;#36. What political issue stirred you the most?&lt;/h2&gt;

&lt;p&gt;I’m not into politics or sports. I don’t like to argue with anyone. Life is too short for arguing.&lt;/p&gt;

&lt;h2 id=&quot;37-who-did-you-miss&quot;&gt;#37. Who did you miss?&lt;/h2&gt;

&lt;p&gt;Mom.&lt;/p&gt;

&lt;h2 id=&quot;38-who-was-the-best-new-person-you-met&quot;&gt;#38. Who was the best new person you met?&lt;/h2&gt;

&lt;p&gt;I didn’t meet many new people in real life. But I met an awarded school teacher, a top cellist, an already FIREd woman, and fellow coders via LinkedIn.&lt;/p&gt;

&lt;h2 id=&quot;39-what-valuable-life-lesson-did-you-learn-this-year&quot;&gt;#39. What valuable life lesson did you learn this year?&lt;/h2&gt;

&lt;p&gt;It’s a cliche until you go through it, but enjoy every moment with your loved ones.&lt;/p&gt;

&lt;p&gt;And it was from &lt;em&gt;Choose Yourself Stories&lt;/em&gt; by James Altucher that I read about treating every day like the last day of your loved one with you.&lt;/p&gt;

&lt;h2 id=&quot;40-what-is-a-quote-that-sums-up-your-year&quot;&gt;#40. What is a quote that sums up your year?&lt;/h2&gt;

&lt;p&gt;I have a couple:&lt;/p&gt;

&lt;p&gt;#1. “One day at a time”&lt;/p&gt;

&lt;p&gt;#2. “There are always enough reasons to keep fighting”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Warm Up Your Network This Holiday Season</title>
   <link href="https://canro91.github.io/2025/12/24/WarmUpYourNetwork/"/>
   <updated>2025-12-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/24/WarmUpYourNetwork</id>
   <content type="html">&lt;p&gt;Your professional network is one of your greatest career assets.&lt;/p&gt;

&lt;p&gt;Most job opportunities aren’t on job boards. They come through favors and phone calls. I know because &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;I’ve found almost all my jobs&lt;/a&gt; by knowing someone who knows someone.&lt;/p&gt;

&lt;p&gt;But don’t &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;wait for a layoff&lt;/a&gt; to reach out for help.&lt;/p&gt;

&lt;p&gt;The holiday season is a good time to “warm up” your connections. This season is for sharing greetings, postcards, and gifts.&lt;/p&gt;

&lt;p&gt;Send a quick hello to old connections: bosses, coworkers. Don’t ask for favors or worse, sell something. Just a casual “how are you” followed by “Merry Christmas” or “Happy New Year.” You can even use the same message for everyone.&lt;/p&gt;

&lt;p&gt;You will surprise them and they’ll keep you top of mind.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Real Sign Your Writing (Or Any Creative Skills) Is Improving</title>
   <link href="https://canro91.github.io/2025/12/23/SignOfGrowth/"/>
   <updated>2025-12-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/23/SignOfGrowth</id>
   <content type="html">&lt;p&gt;I was supposed to unplug during the holidays.&lt;/p&gt;

&lt;p&gt;But I decided to pick an easy project. I’m revamping &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=real-sign-writing-improving&quot;&gt;Unit Testing 101&lt;/a&gt;, the first book I wrote. I called it an ebook because “book” felt too big a deal. Back then, I was even scared to put a price tag on it.&lt;/p&gt;

&lt;p&gt;After finishing &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=real-sign-writing-improving&quot;&gt;Street-Smart Coding&lt;/a&gt;, my first “official” release, I wanted Unit Testing 101 to match that standard: editing, formatting, and a stronger cover.&lt;/p&gt;

&lt;p&gt;Today I opened the draft in Google Docs and realized I’d structure chapters more clearly, tighten explanations, and reduce duplicated concepts.&lt;/p&gt;

&lt;p&gt;If you cringe at your past work and think, “I’d do it way better now,” that’s the clearest sign you’ve grown.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>re: Is &quot;Vibe Coding&quot; Ruining My CS Degree?</title>
   <link href="https://canro91.github.io/2025/12/22/AIRuiningDegrees/"/>
   <updated>2025-12-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/22/AIRuiningDegrees</id>
   <content type="html">&lt;p&gt;It’s tempting to default to AI for its speed.&lt;/p&gt;

&lt;p&gt;If you’re stuck, just write a 2-sentence prompt and after some &lt;em&gt;beep, beep, boop&lt;/em&gt; you get an answer. But relying too much on AI hides &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;a serious problem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve witnessed the over-reliance on AI myself. And it’s worse for CS students. Maame Afua A. P. Fordjour shared in &lt;a href=&quot;https://dev.to/maame-codes/is-vibe-coding-ruining-my-cs-degree-3m3&quot;&gt;a dev.to post&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;[When facing a Data Structures assignment] I type: “Yo, I need a Red-Black Tree implementation in C++. Keep the vibe academic, handle the edge cases for rotation, and add comments that sound like a stressed undergrad wrote them.”&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;Thirty seconds later. Done.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;It compiles. It passes the test cases. It’s beautiful code. And I have absolutely no idea how it works.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;its-not-the-tool-but-how-you-use-it&quot;&gt;It’s not the tool, but how you use it&lt;/h2&gt;

&lt;p&gt;Like we always say: the problem isn’t the tool, but how we use it.&lt;/p&gt;

&lt;p&gt;AI isn’t going anywhere. It’s already changing our job descriptions. But we need strong guidelines to avoid blindly trusting AI and losing our skills.&lt;/p&gt;

&lt;p&gt;If you’re a confident coder, write your method signatures, use comments to sketch their bodies, and let AI fill in the blanks.&lt;/p&gt;

&lt;p&gt;But if you’re a beginner, &lt;a href=&quot;/2025/09/15/ShouldIUseAI/&quot;&gt;my take is radical&lt;/a&gt;: &lt;em&gt;don’t use AI to generate code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Don’t use AI to do your homework. Use it as your teaching assistant instead. To quiz you, generate test cases, explain a tricky code block. Otherwise it’s like sending someone else to the gym and then complaining when you don’t see your muscles growing.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/11/27/HowToThinkOfAI/&quot;&gt;AI is like a powerful calculator&lt;/a&gt;. You need strong math skills first before using one.&lt;/p&gt;

&lt;p&gt;If you want to strengthen your coding skills, my book &lt;em&gt;Street-Smart Coding&lt;/em&gt; covers 30 lessons to help you level up, from Googling to communication to problem-solving.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=vibe-coding-ruining-my-degree&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;—That’s the roadmap I wish I had  on my journey to senior coder.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What Coders Could Offer Instead of Writing Lines of Code If AI Takes Over</title>
   <link href="https://canro91.github.io/2025/12/21/Offerings/"/>
   <updated>2025-12-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/21/Offerings</id>
   <content type="html">&lt;p&gt;What happens when AI codes better than we do?&lt;/p&gt;

&lt;p&gt;For a second, let’s forget AI is more like &lt;a href=&quot;/2025/10/20/SloppyAI/&quot;&gt;a sloppy junior coder&lt;/a&gt;. Let’s stop &lt;a href=&quot;/2025/11/11/Cheating/&quot;&gt;feeling like cheating&lt;/a&gt; when prompting instead of typing. If AI writes reliable code, what would we offer as coders?&lt;/p&gt;

&lt;p&gt;In a &lt;a href=&quot;/2025/12/19/FridayLinks/&quot;&gt;past Friday Links&lt;/a&gt;, I shared one post from Leon Mika that made me ask those questions.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lmika.org/2025/12/14/maybe-writing-code-is-no.html&quot;&gt;Leon wrote&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Maybe writing code is no longer part of my “core offering” at this point in my career. Maybe it is the “judgement, tradeoffs, intents” and all the other buzzwords people throw around when describing a senior software engineer.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Leon’s post made me think what we could offer. Here’s my list:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Talking to end users to find out what they need&lt;/li&gt;
  &lt;li&gt;Choosing what features to implement&lt;/li&gt;
  &lt;li&gt;Choosing the right tools, stack, and frameworks&lt;/li&gt;
  &lt;li&gt;Scoping projects into milestones or sprints&lt;/li&gt;
  &lt;li&gt;Finding what to rewrite and when&lt;/li&gt;
  &lt;li&gt;Coming up with de-risking plans&lt;/li&gt;
  &lt;li&gt;Choosing the right time to scale&lt;/li&gt;
  &lt;li&gt;Sharing past mistakes and lessons&lt;/li&gt;
  &lt;li&gt;Vetting what to build, buy, or outsource&lt;/li&gt;
  &lt;li&gt;Finding cost-effective “cloudification” strategies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some coders already do those tasks, but soon they’ll be everyday work for all of us.&lt;/p&gt;

&lt;p&gt;In the meantime, &lt;a href=&quot;/2025/11/01/AIGift/&quot;&gt;AI is making us rediscover the practice of coding&lt;/a&gt;. And that’s already a good point for AI.&lt;/p&gt;

&lt;p&gt;When AI shines at coding, we need strong product thinking, communication, and other skills I cover in &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I had to become a senior coder.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=what-coders-could-offer-instead-of-writing-code&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code 2025 in Summary</title>
   <link href="https://canro91.github.io/AdventOfCode2025"/>
   <updated>2025-12-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/AdventOfCode</id>
   <content type="html">&lt;p&gt;I ran my first Advent challenge in 2022.&lt;/p&gt;

&lt;p&gt;Instead of an Advent of Code, I ran &lt;a href=&quot;/AdventOfCode2022&quot;&gt;an Advent of Posts&lt;/a&gt;. I wrote 22 posts, mostly about coding, in the days before Christmas. I missed two days but I declared the mission complete.&lt;/p&gt;

&lt;p&gt;This year, I decided to follow the &lt;a href=&quot;https://adventofcode.com/&quot;&gt;Advent of Code&lt;/a&gt;. For the first time, this year included only 12 puzzles instead of 25.&lt;/p&gt;

&lt;p&gt;I challenged myself to write “functionalish” solutions. Sometimes that was a bad idea.&lt;/p&gt;

&lt;h2 id=&quot;it-wasnt-a-bunch-of-leetcode-problems&quot;&gt;It wasn’t a bunch of LeetCode problems&lt;/h2&gt;

&lt;p&gt;The puzzles weren’t disconnected problems.&lt;/p&gt;

&lt;p&gt;Every day started with a short story. The first day we arrived at the North Pole. Right away, we faced a setback that made us solve a puzzle. As we got inside the North Pole, there was always a complication.&lt;/p&gt;

&lt;p&gt;In spite of the problems, elves were always organized. By the time we helped, they already had sorted out some details. That was our puzzle input. I guess elves are lazy or simply distracted.&lt;/p&gt;

&lt;p&gt;There was a connecting story from one day to the next.&lt;/p&gt;

&lt;h2 id=&quot;it-wasnt-funny-some-days&quot;&gt;It wasn’t funny some days&lt;/h2&gt;

&lt;p&gt;My goal going through the challenge was to ignite my coding spark.&lt;/p&gt;

&lt;p&gt;But some puzzles had ambiguous instructions or I overcomplicated the solution trying to follow a functional approach. I got stuck solving &lt;a href=&quot;/2025/12/14/AoC8/&quot;&gt;Day 8&lt;/a&gt; and &lt;a href=&quot;/2025/12/17/AoC11/&quot;&gt;Day 11&lt;/a&gt;. I had to look up clues to get them done. Arrggg!&lt;/p&gt;

&lt;p&gt;Those days, I got stuck with &lt;a href=&quot;/2025/11/18/BlazorValidation/&quot;&gt;a stupid Blazor issue&lt;/a&gt; at work. Getting stuck after work wasn’t pleasant at all. The challenge was supposed to be funny and enjoyable. I didn’t need another source of stress.&lt;/p&gt;

&lt;h2 id=&quot;my-solutions&quot;&gt;My solutions&lt;/h2&gt;

&lt;p&gt;Despite the tough days, I eventually solved them all. OK, I cheated a bit.&lt;/p&gt;

&lt;p&gt;Here are my solutions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Day 1: &lt;a href=&quot;/2025/12/02/AoC1/&quot;&gt;Unlocking the North Pole&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 2: &lt;a href=&quot;/2025/12/03/AoC2/&quot;&gt;Finding Invalid Product IDs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 3: &lt;a href=&quot;/2025/12/04/AoC3/&quot;&gt;Calculating the Joltage of Batteries&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 4: &lt;a href=&quot;/2025/12/07/AoC4/&quot;&gt;Counting Rolls of Paper&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 5: &lt;a href=&quot;/2025/12/10/AoC5/&quot;&gt;Counting Fresh Ingredients&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 6: &lt;a href=&quot;/2025/12/11/AoC6/&quot;&gt;Doing Cephalopods’ Math&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 7: &lt;a href=&quot;/2025/12/13/AoC7/&quot;&gt;Splitting Tachyon Beams&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 8: &lt;a href=&quot;/2025/12/14/AoC8/&quot;&gt;Connecting Junction Boxes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 9: &lt;a href=&quot;/2025/12/15/Aoc9/&quot;&gt;Finding the Largest Rectangle of Red Tiles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 10: &lt;a href=&quot;/2025/12/16/AoC10/&quot;&gt;Initializing Factory Machines&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 11: &lt;a href=&quot;/2025/12/17/AoC11/&quot;&gt;Connecting the Reactor With a Server Rack&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 12: &lt;a href=&quot;/2025/12/18/AoC12/&quot;&gt;Packaging Presents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From all the puzzles, one stood out. Day 12. It was playing Tetris with presents. My first thought was brute force with backtracking. After struggling with Day 11, I knew there was a simple alternative. And my intuition paid off.&lt;/p&gt;

&lt;h2 id=&quot;biggest-lessons&quot;&gt;Biggest lessons&lt;/h2&gt;

&lt;p&gt;Apart from collecting more extension methods, here are the biggest lessons I took away:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Always strive for simple, stupid solutions.&lt;/li&gt;
  &lt;li&gt;LINQ can take you a long way. I solved almost all the puzzles using LINQ.&lt;/li&gt;
  &lt;li&gt;Review data structures. Once more, &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;I was trolled by trees&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See you next year for another Advent of Code or maybe another Advent of Posts.&lt;/p&gt;

&lt;p&gt;Advent of Code really sharpens your problem-solving skills. But coding isn’t just about puzzles. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;. That’s the roadmap I wish I’d known from day one to become a senior coder.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Coding, Space Jam, and people problems</title>
   <link href="https://canro91.github.io/2025/12/19/FridayLinks/"/>
   <updated>2025-12-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/19/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, I didn’t miss my reading time this week. Here are 4 interesting links for you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; One big lesson I’ve learned after over a decade in coding is &lt;a href=&quot;https://blog.joeschrag.com/2023/11/most-technical-problems-are-really.html&quot;&gt;most technical problems are people problems&lt;/a&gt; (5min). Most team problems trace back to poor communication. Just like marriages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; I watched Space Jam countless times as a kid. I remember it because &lt;a href=&quot;https://j0nah.com/i-failed-to-recreate-the-1996-space-jam-website-with-claude&quot;&gt;a coder tried to recreate its website with Claude and failed&lt;/a&gt; (10min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Blogging isn’t dead. There are still indie bloggers in a small corner of the Internet. Here are &lt;a href=&quot;https://alexsci.com/blog/calm-tech-discover/&quot;&gt;two ideas to discover indie bloggers&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; With AI spitting out code faster than any of us, &lt;a href=&quot;https://lmika.org/2025/12/14/maybe-writing-code-is-no.html&quot;&gt;writing code may not be our main offering as coders&lt;/a&gt; (2min), but judgment, past failed projects, and risk management.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;By now, I’ve finished the Advent of Code. Two days were tough. Instructions weren’t clear enough, or I overcomplicated my solutions. Here are two days where I got stuck: &lt;a href=&quot;/2025/12/14/AoC8/&quot;&gt;Day 8&lt;/a&gt; (5min) and &lt;a href=&quot;/2025/12/17/AoC11/&quot;&gt;Day 11&lt;/a&gt; (2min). I ended up looking at solutions to get unstuck.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… my new book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 12: Packaging Presents</title>
   <link href="https://canro91.github.io/2025/12/18/AoC12/"/>
   <updated>2025-12-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/18/AoC12</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/12&quot;&gt;Day 12&lt;/a&gt; of Advent of Code, we’re helping to place presents under trees for Christmas. Basically, we’re playing Tetris.&lt;/p&gt;

&lt;p&gt;After &lt;a href=&quot;/2025/12/17/AoC11/&quot;&gt;yesterday’s struggle&lt;/a&gt;, my first reaction was thinking it must be something simple instead of brute-forcing positions and rotations plus backtracking.&lt;/p&gt;

&lt;p&gt;My first rationale was checking if the areas of presents was less than the available area. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7*numberOfShapes &amp;lt; width*height&lt;/code&gt;. By looking at other solutions, it seems that was enough.&lt;/p&gt;

&lt;p&gt;This last puzzle felt…weird? I knew there was a simple solution after the struggle of previous days. And after looking at others’ solution, I feel like I missed the class on packaging algorithms. Everybody mentioned them in the solutions.&lt;/p&gt;

&lt;p&gt;Today is another bearly voilà.&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-12&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 11: Connecting the Reactor With a Server Rack</title>
   <link href="https://canro91.github.io/2025/12/17/AoC11/"/>
   <updated>2025-12-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/17/AoC11</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/11&quot;&gt;Day 11&lt;/a&gt; of Advent of Code, we’re helping to connect a reactor with a server rack by following the path between devices.&lt;/p&gt;

&lt;p&gt;Day 11 made me feel like an impostor. Since it involved a graph (one device outputs to multiple devices), I imagined a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetAllPaths()&lt;/code&gt; function and &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;a LINQ query&lt;/a&gt; to count paths ending with “out.”&lt;/p&gt;

&lt;p&gt;After more than 2 hours struggling with a recursive function, I looked at others’ solutions and realized I was overcomplicating it.&lt;/p&gt;

&lt;p&gt;Yes, the solution used recursion. But it was simpler than I thought.&lt;/p&gt;

&lt;p&gt;Here’s my full solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connections&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;aaa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;you&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hhh&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;you&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;bbb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ccc&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bbb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;ddd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;eee&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ccc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;ddd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;eee&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fff&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ddd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;ggg&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;eee&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;out&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fff&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;out&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ggg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;out&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hhh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;ccc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fff&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;iii&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;iii&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;&quot;out&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paths&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CountPaths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;you&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;out&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CountPaths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;starting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paths&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;paths&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CountPaths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà! Barely. Most people found today a warm-up after &lt;a href=&quot;/2025/12/16/AoC10/&quot;&gt;yesterday’s puzzle&lt;/a&gt;. But for me, it was the harder day. Trying to be too functional made me overcomplicate everything. Simple always wins, especially for Advent of Code.&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-11&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 10: Initializing Factory Machines</title>
   <link href="https://canro91.github.io/2025/12/16/AoC10/"/>
   <updated>2025-12-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/16/AoC10</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/10&quot;&gt;Day 10&lt;/a&gt; of Advent of Code, we’re helping to turn on factory machines by configuring light indicators.&lt;/p&gt;

&lt;p&gt;Each machine has buttons that toggle indicators, and the challenge is figuring out the minimal combination that produces the target pattern.&lt;/p&gt;

&lt;p&gt;To model this, I define a machine as a list of booleans representing its light indicators. Then, I write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Push()&lt;/code&gt; to toggle light indicators at a given index.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Machine&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To configure a machine, I use &lt;a href=&quot;https://stackoverflow.com/a/57058345&quot;&gt;this StackOverflow answer&lt;/a&gt; to generate all button combinations and apply them to reach the target indicator pattern.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureIndicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Machine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turnOn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SequenceEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;turnOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turnOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MinBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/2022/07/25/LinqAggregateExplained/&quot;&gt;LINQ Aggregate method&lt;/a&gt; to the rescue here!&lt;/p&gt;

&lt;p&gt;Unlike the puzzle instructions, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buttons.Combinations()&lt;/code&gt; I’m assuming I can only toggle a sequence of buttons once.&lt;/p&gt;

&lt;p&gt;So far, I can write,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureIndicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// presses.Length&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And this puzzle is almost a voilà moment. I skip parsing the diagrams and just hard‑code them, then sum the button‑press lengths. Yes, I’m a bit lazy.&lt;/p&gt;

&lt;p&gt;Here’s my full solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureIndicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presses2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureIndicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presses3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureIndicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presses2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;presses3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureIndicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Machine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turnOn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SequenceEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;turnOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;turnOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MinBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Machine&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Machine&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buttons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pushed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Indicators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;#&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Extensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And finally…et voilà! The factory lights up, and Day 10 is complete.&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-10&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 9: Finding the Largest Rectangle of Red Tiles</title>
   <link href="https://canro91.github.io/2025/12/15/Aoc9/"/>
   <updated>2025-12-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/15/Aoc9</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/9&quot;&gt;Day 9&lt;/a&gt; of Advent of Code, we’re helping elves to redecorate the theater by rearranging the red tiles on the floor.&lt;/p&gt;

&lt;p&gt;After &lt;a href=&quot;/2025/12/13/AoC7/&quot;&gt;yesterday’s struggle&lt;/a&gt;, this is an easy one when I consider every pair of points as the opposite corners of a rectangle to calculate its area. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Area&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, I reuse &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pairs()&lt;/code&gt; from yesterday to find all rectangles. Next, I use &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;LINQ’s MaxBy&lt;/a&gt; to find the largest one. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;largest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redTiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MaxBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With those two pieces, the puzzle is solved. Here’s my full solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redTiles&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;largest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redTiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MaxBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;largest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Area&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Extensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I deserved some rest from yesterday’s puzzle. Today, I can easily say: Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-9&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 8: Connecting Junction Boxes</title>
   <link href="https://canro91.github.io/2025/12/14/AoC8/"/>
   <updated>2025-12-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/14/AoC8</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/8&quot;&gt;Day 8&lt;/a&gt; of Advent of Code, we’re helping elves with some decorations, so we’re connecting junction boxes.&lt;/p&gt;

&lt;p&gt;Unlike the first 7 days, I cheated. Yes! This puzzle wasn’t fun anymre after spending a couple of hours to make the example work. In a moment of frustration, I went to Reddit for some help and I reworked my original solution.&lt;/p&gt;

&lt;p&gt;First, I tried to create circuits as I visited pair of junction boxes. But, after getting some help from Reddit, every junction box is a 1-box circuit to then merge all the pairs.&lt;/p&gt;

&lt;h2 id=&quot;finding-distances&quot;&gt;Finding distances&lt;/h2&gt;

&lt;p&gt;To find the distance between all possibles pair of junction boxes, I borrowed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pairs()&lt;/code&gt; method from &lt;a href=&quot;/2025/12/04/AoC3/&quot;&gt;Day 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To calculate the distance, I use the squared of the distance as a small optimization.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distances&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allBoxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Extensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;connecting-boxes&quot;&gt;Connecting boxes&lt;/h2&gt;

&lt;p&gt;A circuit is a wrapper around a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashSet&lt;/code&gt; with a method to merge two circuits.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Circuit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the list of junction boxes by distance and a circuit, here’s the full solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allBoxes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;162&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;817&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;812&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;57&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;618&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;57&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;906&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;560&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;592&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;479&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;940&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;352&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;342&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;466&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;668&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;158&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;542&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;29&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;236&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;431&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;825&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;739&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;650&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;466&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;470&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;668&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;216&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;146&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;977&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;819&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;987&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;117&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;168&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;530&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;805&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;96&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;715&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;346&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;949&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;466&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;970&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;615&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;88&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;941&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;993&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;340&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;862&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;61&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;984&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;92&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;344&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;425&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;690&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;689&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allBoxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distances&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allBoxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasFirstBox&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasSecondBox&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasFirstBox&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasSecondBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;hasFirstBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasSecondBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;circuits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasSecondBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JunctionBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Circuit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;circuit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JunctionBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Extensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-8&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 7: Splitting Tachyon Beams</title>
   <link href="https://canro91.github.io/2025/12/13/AoC7/"/>
   <updated>2025-12-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/13/AoC7</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/7&quot;&gt;Day 7&lt;/a&gt; of Advent of Code, we’re studying tachyon beams while we’re stuck in a teleporter.&lt;/p&gt;

&lt;p&gt;I imagine this puzzle as a game where each iteration moves the beam until it hits the end of the manifold.&lt;/p&gt;

&lt;h2 id=&quot;moving-a-beam&quot;&gt;Moving a beam&lt;/h2&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Beam&lt;/code&gt; is a list of positions in the manifold at a given iteration.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Locations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the method to move a beam,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Beam&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Beam&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Locations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;downward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;downward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;downward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After moving a beam, I need the entry position of a beam,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Beam&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;S&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With those two methods, a beam enters and moves downward,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;newPosition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All that’s left is to count splits and move the beam to the end.&lt;/p&gt;

&lt;h2 id=&quot;counting-splits&quot;&gt;Counting splits&lt;/h2&gt;

&lt;p&gt;Next, I add the number of splits to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Beam&lt;/code&gt; and create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasReacheTheEnd()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s my full solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;S&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasReachedTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SplitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Beam&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;S&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasReachedTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Beam&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anyBeam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Locations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anyBeam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Beam&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Beam&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;splits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Locations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;downward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;downward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;downward&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;splits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SplitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;splits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newLocations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Beam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SplitCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Locations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I thought a “tachyon” was a made-up word until I Googled it. TIL what a tachyon is. Win-win.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-7&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Becoming a senior dev, coding, and phones</title>
   <link href="https://canro91.github.io/2025/12/12/FridayLinks/"/>
   <updated>2025-12-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/12/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, it’s been a busy week. I’ve been preparing my last book for publish on Amazon, so I didn’t read that much.&lt;/p&gt;

&lt;p&gt;But I still want to share some interesting reads. Here are my top 3 most read posts from the quarter plus a funny weekend project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; I always joke that I don’t know when “Senior” made it into my job title. But I know it was thanks to good mentors. Here are &lt;a href=&quot;/2025/10/25/LessonsFromMentor/&quot;&gt;5 lessons from one of my mentors&lt;/a&gt; (4min). I’ve been following lesson #1 since I met him…Well, not in the past week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; What’s the first thing that comes to mind when you hear “get good at coding”? Younger me only thought about learning programming languages. But here are &lt;a href=&quot;/2025/10/26/GettingGood/&quot;&gt;7 unconventional ways to improve your coding skills&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; I’d do a lot differently if I were a junior again. Here’s &lt;a href=&quot;/2025/08/13/OneCodingLesson/&quot;&gt;one lesson I wish I had known 10 years ago&lt;/a&gt; (3min). That would have made me a well-rounded coder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; What if we could slam a phone after a boring Zoom call? That was the side project of someone who &lt;a href=&quot;https://www.stavros.io/posts/i-converted-a-rotary-phone-into-a-meeting-handset/&quot;&gt;turned a rotary phone into a meeting handset&lt;/a&gt; (8min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Speaking of coding, are you following the Advent of Code? I have. I missed a couple of days, but I’m catching up this weekend. Here’s my last solution: &lt;a href=&quot;https://canro91.github.io/2025/12/10/AoC6/&quot;&gt;Advent of Code Day 6: Doing Cephalopods’ Math&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… my new book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on &lt;a href=&quot;https://www.amazon.com/dp/B0G64PG9NP&quot;&gt;Kindle and paperback on Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 6: Doing Cephalopods&apos; Math</title>
   <link href="https://canro91.github.io/2025/12/11/AoC6/"/>
   <updated>2025-12-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/11/AoC6</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/6&quot;&gt;Day 6&lt;/a&gt; of Advent of Code, we’re helping some cephalopods with their kids’ math homework…while they open a trash compactor.&lt;/p&gt;

&lt;p&gt;First, I define a problem and function to solve it. A problem is an operation and some numbers.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Problem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Multiply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Multiply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cephalopods’ math is weird with operations and operands organized vertically, not horizontally. Parsing is the trick part: the input is a 2D array where each column is a problem. Numbers above and operator at the bottom.&lt;/p&gt;

&lt;p&gt;I loop through the array of arrays column-wise. The last element becomes the operation, and the rest become the numbers,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsedProblems&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[^&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;+&quot;&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Multiply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;parsedProblems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsedProblems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the parsing in place, Day 6 is almost done. Here’s my final solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;328&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;51&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;64&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;45&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;64&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;387&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;23&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;6&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;98&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;215&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;314&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;+&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsedProblems&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;problems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[^&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;+&quot;&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Multiply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;parsedProblems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsedProblems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Problem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Multiply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Multiply&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Problem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Two one-off bugs got me this time, but I finally cracked it. The parsing part isn’t that functional in style, but “it works on my machine.” Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-6&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 5: Counting Fresh Ingredients</title>
   <link href="https://canro91.github.io/2025/12/10/AoC5/"/>
   <updated>2025-12-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/10/AoC5</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/5&quot;&gt;Day 5&lt;/a&gt; of Advent of Code, we’re helping the elves to find which ingredients are fresh or spoiled.&lt;/p&gt;

&lt;p&gt;To find if an ingredient is fresh, I create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FreshIngredientRange&lt;/code&gt; with a method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsFresh()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsFresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, to count all the fresh ingredients, I write a method that receives all product ranges and all candidate products. A single LINQ query will do the counting,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CountFreshIngredients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsFresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s my final solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productIds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CountFreshIngredients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CountFreshIngredients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsFresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FreshIngredientRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsFresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;productId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IngredientId2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s an easy one with LINQ. Just one single line. Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-5&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Quick Lesson After A Long Debugging Session (And Almost Pulling My Hair Off)</title>
   <link href="https://canro91.github.io/2025/12/09/Debugging/"/>
   <updated>2025-12-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/09/Debugging</id>
   <content type="html">&lt;p&gt;I almost pulled my hair off.&lt;/p&gt;

&lt;p&gt;I debugged an issue in a Blazor grid for over two half days. I followed my own advice from &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=quick-lesson-long-debugging-session&quot;&gt;Street-Smart Coding&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Isolated the problem&lt;/li&gt;
  &lt;li&gt;Removed all irrelevant parts&lt;/li&gt;
  &lt;li&gt;Discussed it with a rubber duck&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just to keep seeing the same error message: &lt;em&gt;TypeError: Cannot read properties of null (reading ‘removeChild’)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;StackOverflow says it was Blazor trying to remove an orphan element. So I removed everything except for my grid and wrapped it around a div.&lt;/p&gt;

&lt;p&gt;Same mistake.&lt;/p&gt;

&lt;p&gt;After questioning my career choices and almost removing “Senior” from my title, I asked for help.&lt;/p&gt;

&lt;p&gt;My coworker pulled my branch and reproduced the issue. To my surprise, the issue wasn’t only in my grid. It was in all other grids, all over the app. It was an issue in the Blazor component itself we were using for grids. Arrggg!&lt;/p&gt;

&lt;p&gt;Sometimes you just need to ask for help earlier. Like &lt;a href=&quot;/2025/10/25/LessonsFromMentor/&quot;&gt;one of my mentors told me and the team&lt;/a&gt;, &lt;em&gt;“you have nothing to prove. Ask for help.”&lt;/em&gt; And that’s something I cover on Chapter #2 of &lt;em&gt;Street-Smart Coding&lt;/em&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Inspiring Business Lessons From Repairing a Blender (And Why I Almost Quit Online Hustles)</title>
   <link href="https://canro91.github.io/2025/12/08/RepairingBlender/"/>
   <updated>2025-12-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/08/RepairingBlender</id>
   <content type="html">&lt;p&gt;My blender broke. Nothing unusual, until I took it to the only repair shop nearby.&lt;/p&gt;

&lt;p&gt;Like any repair shop, it was greasy and hot, with shelves full of small parts… and crowded. It was so packed with people carrying blenders and fans, that I had to wait for a technician.&lt;/p&gt;

&lt;p&gt;While waiting for a receipt I learned two business lessons that make me want to start a blender shop too:&lt;/p&gt;

&lt;h2 id=&quot;1-forget-about-fancy-businesses-go-with-a-boring-one&quot;&gt;1. Forget about fancy businesses. Go with a boring one&lt;/h2&gt;

&lt;p&gt;Fixing blenders is boring.&lt;/p&gt;

&lt;p&gt;You don’t dream about going to business school to start a blender-repair shop. But it’s recession‑proof and even resistant to AI “disruption.” I knew it was a good business immediately.&lt;/p&gt;

&lt;p&gt;Almost every house in my town has a blender. With a good location and an attractive name, like “BlenderCenter,” this place won’t run out of potential clients ever.&lt;/p&gt;

&lt;p&gt;While I waited, an old lady offered coffee and cold juice to everyone in line. A small gesture, but great customer service. Not like &lt;a href=&quot;/2025/07/30/BuyingAMattress/&quot;&gt;buying a mattress the other day&lt;/a&gt;.  No wonder this shop never runs out of clients.&lt;/p&gt;

&lt;p&gt;Forget about crypto, NFTs, and dropshipping. Go with a boring business with clients.&lt;/p&gt;

&lt;h2 id=&quot;2-stick-with-simple-reliable-tools&quot;&gt;2. Stick with simple, reliable tools&lt;/h2&gt;

&lt;p&gt;When I got to the cashier for my receipt, I was surprised.&lt;/p&gt;

&lt;p&gt;I looked into the lady’s computer. I expected a fancy point-of-sale software with bells and whistles. Nope!&lt;/p&gt;

&lt;p&gt;The entire place runs with Microsoft Access. That’s a simple database tool. I could notice it: the window, bars, buttons, and the look and feel of the app. But, it registered me as a new user, created a new order, and printed a receipt… With Access. I remember learning to build forms and databases with Access in high school.&lt;/p&gt;

&lt;p&gt;New business owners are tempted to go with the shiniest tools to start a business. Being a coder by day, you don’t know how many friends and acquaintances have asked me to &lt;a href=&quot;/2025/09/17/ValidateIdeas/&quot;&gt;quote for custom software before starting their business&lt;/a&gt;. Honestly, I’d just tell them: start with Access.&lt;/p&gt;

&lt;p&gt;Prefer simple, reliable tools to run your business.&lt;/p&gt;

&lt;p&gt;I never imagined a blender-repair shop could be so inspiring and full of lessons. Meanwhile, I’m still trying to &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;make money online&lt;/a&gt;. Maybe I should ditch the online hustles and start a blender-repair shop instead. LOL!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 4: Counting Rolls of Paper</title>
   <link href="https://canro91.github.io/2025/12/07/AoC4/"/>
   <updated>2025-12-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/07/AoC4</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/4&quot;&gt;Day 4&lt;/a&gt; of Advent of Code, we’re finding which rolls of paper forklifts can access, so we can get further into the North Pole base.&lt;/p&gt;

&lt;p&gt;I model the diagram of rolls of paper as a boolean grid. A cell is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; if it has a roll.&lt;/p&gt;

&lt;h2 id=&quot;finding-adjacent-positions&quot;&gt;Finding adjacent positions&lt;/h2&gt;

&lt;p&gt;To find the adjacent positions, I add -1, 0, 1 to each coordinate, with proper bounds checking.&lt;/p&gt;

&lt;p&gt;Here’s my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FindAdjacentPositions()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindAdjacentPositions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, the next step is filtering and counting the rolls with less than 4 neighbor rolls.&lt;/p&gt;

&lt;h2 id=&quot;filtering-and-counting&quot;&gt;Filtering and counting&lt;/h2&gt;

&lt;p&gt;Here’s my final solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;adjacents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindAdjacentPositions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;adjacents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roll&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindAdjacentPositions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[][]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rollsOfPaper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My goal is to use “functionalish” solutions, but two loops and an accumulator were so easy that I stuck with them.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-4&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My 400 Daily Posts Reflection</title>
   <link href="https://canro91.github.io/2025/12/06/400DailyPosts/"/>
   <updated>2025-12-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/06/400DailyPosts</id>
   <content type="html">&lt;p&gt;Writing 400 daily posts gave me momentum as a writer.&lt;/p&gt;

&lt;p&gt;The first 100 posts were training to find ideas. The next 100 proved I wouldn’t run out of ideas. The next 100 tested my discipline, I wrote even when tired. This last 100 gave me trust to keep going.&lt;/p&gt;

&lt;p&gt;In the last 3 months, I released a book, &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt; and I shared the behind-the-scenes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/11/16/LaunchingABook/&quot;&gt;Some hard-earned lessons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/11/22/HardTruthsWritingABook/&quot;&gt;Some truths nobody told me&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/09/BetaReaders/&quot;&gt;my strategy to work with beta readers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/04/ABookFaster/&quot;&gt;how to finish the next one faster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Promoting my book won me &lt;a href=&quot;/2025/11/02/Haters/&quot;&gt;my first hater&lt;/a&gt;, a milestone in the writing journey.&lt;/p&gt;

&lt;p&gt;But beyond criticism, writing daily gave me something more lasting: &lt;em&gt;“the time capsule”&lt;/em&gt; effect.&lt;/p&gt;

&lt;p&gt;And after many posts, I can see what I was learning each day years ago. I even started to write some “on this day” posts. Here’s &lt;a href=&quot;/2025/10/05/AroundThisDate/&quot;&gt;the last one&lt;/a&gt; I wrote.&lt;/p&gt;

&lt;p&gt;That’s a gift for future generations: reading what grandpa wrote 20 years ago.&lt;/p&gt;

&lt;h2 id=&quot;my-most-read-and-favorite-posts&quot;&gt;My most-read and favorite posts&lt;/h2&gt;

&lt;p&gt;Here are some of the most-read posts from the last 100 days:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/25/LessonsFromMentor/&quot;&gt;5 Lessons from My Team’s Architect That Helped Me Become a Senior Developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;The One Rule I Follow to Code with AI (Without Losing My Skills)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;Don’t Write the Next Atomic Habits. Write Mini-Books Instead&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;10 Lessons From Steal Like an Artist That Transformed My Creativity&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/09/08/StoicBookReading/&quot;&gt;8 Reading Habits I Learned from Ryan Holiday, the Stoic Book Master&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/11/29/Fighting/&quot;&gt;Four Lessons I Learned While Standing By My Mom’s Battle With Chronic Disease&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are my &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;100-post&lt;/a&gt;, &lt;a href=&quot;/2025/05/20/200DailyPosts/&quot;&gt;200-post&lt;/a&gt;, and &lt;a href=&quot;/2025/09/01/300DailyPosts/&quot;&gt;300-post&lt;/a&gt;. Next milestone to celebrate: 500 daily posts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Confessions, manager pendulum, and the junior hiring crisis</title>
   <link href="https://canro91.github.io/2025/12/05/FridayLinks/"/>
   <updated>2025-12-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/05/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey! Here are 3 reads (plus a funny bonus) I found interesting this week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; After being afraid of sharing them, here are &lt;a href=&quot;https://kerrick.blog/articles/2025/confessions-of-a-software-developer-no-more-self-censorship/&quot;&gt;the confessions of an uncensored coder&lt;/a&gt; (10min). I agree, we don’t have to learn everything about everything. And I have to confess I don’t fully understand Blazor, in spite of working with it for almost a year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Most companies only offer two tracks for coders. You either stay as an Individual Contributor or join the Management side. Switching between the two is called: “manager pendulum.” But &lt;a href=&quot;https://www.modernleader.is/p/pendulum-revisited&quot;&gt;AI might be breaking the pendulum&lt;/a&gt; (10min), creating hybrid roles. Or maybe it’s just an excuse to get rid of the role and the compensation?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; It’s easier than ever to start coding. But it’s getting harder and harder to break into the market as a junior coder. &lt;a href=&quot;https://people-work.io/blog/junior-hiring-crisis/&quot;&gt;We’re in a junior hiring crisis&lt;/a&gt; (10min). AI seems to have a lot with it…And just in the past few weeks, someone in the same situation reached out on LinkedIn asking for advice. I didn’t have much to say.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus:&lt;/strong&gt; What if we let AI replace our bosses? Here’s &lt;a href=&quot;https://replaceyourboss.ai/&quot;&gt;replaceyourboss.ai&lt;/a&gt;. The funniest line I got: “Let’s cut the single-use cups but keep the private jet for strategic mobility.”&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;On my blog, this past week, I shared about &lt;a href=&quot;/2025/12/01/TooMuchAI/&quot;&gt;4 AI issues I’ve collected&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/11/29/Fighting/&quot;&gt;4 lessons I learned while fighting next to my mom with a chronic disease&lt;/a&gt; (3min). Feel free to skip that last one if you only want to read about coding. I won’t get mad.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… My latest book, &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-singleletter-names-windows-optimization&quot;&gt;Street-Smart Coding&lt;/a&gt;: 30 lessons to help you code like a pro, from Googling to clear communication. It’s the roadmap I wish I had moving from junior to senior and the one I hope helps you too.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 3: Calculating the Joltage of Batteries</title>
   <link href="https://canro91.github.io/2025/12/04/AoC3/"/>
   <updated>2025-12-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/04/AoC3</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/3&quot;&gt;Day 3&lt;/a&gt; of Advent of Code, we’re finding the joltage of a bank of batteries to power an escalator.&lt;/p&gt;

&lt;p&gt;Elevators are out of service and the escalator needs the batteries with the higher joltage.&lt;/p&gt;

&lt;h2 id=&quot;finding-pairs-of-batteries&quot;&gt;Finding pairs of batteries&lt;/h2&gt;

&lt;p&gt;I use brute force to find all battery pairs.&lt;/p&gt;

&lt;p&gt;Two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loops didn’t feel old-schoool. So I borrow a LINQ query from &lt;a href=&quot;https://stackoverflow.com/a/47003122&quot;&gt;this StackOverflow answer&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Extensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;calculating-the-joltage&quot;&gt;Calculating the joltage&lt;/h2&gt;

&lt;p&gt;Once we have the pairs, I need two methods to calculate joltage: for a pair and for a bank.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindJoltage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bank&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Batteries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindJoltageOfPair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindJoltageOfPair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Battery1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Battery2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Battery1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Battery2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s my full solution:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allBanks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totalJoltage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allBanks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindJoltage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;totalJoltage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindJoltage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bank&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Batteries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindJoltageOfPair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindJoltageOfPair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Battery1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Battery2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Battery1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Battery2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bank&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Batteries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Extensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-3&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 2: Finding Invalid Product IDs</title>
   <link href="https://canro91.github.io/2025/12/03/AoC2/"/>
   <updated>2025-12-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/03/AoC2</id>
   <content type="html">&lt;p&gt;On &lt;a href=&quot;https://adventofcode.com/2025/day/2&quot;&gt;Day 2&lt;/a&gt; of Advent of Code, we’re helping the Elves find invalid IDs.&lt;/p&gt;

&lt;p&gt;I considered a string-based solution, but that felt like cheating. So I tried a “numeric” one instead.&lt;/p&gt;

&lt;p&gt;Let’s start with a bottom-up approach.&lt;/p&gt;

&lt;h2 id=&quot;finding-if-a-product-id-is-invalid&quot;&gt;Finding if a product ID is invalid&lt;/h2&gt;

&lt;p&gt;First, I count the digits of a number, then split the number in half using powers of 10.&lt;/p&gt;

&lt;p&gt;I borrowed &lt;a href=&quot;https://stackoverflow.com/a/1306751&quot;&gt;this StackOverflow answer&lt;/a&gt; to count digits.&lt;/p&gt;

&lt;p&gt;And since a product ID is invalid if it has a sequence repeated twice, product IDs with an odd number of digits are valid.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsAnInvalidId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasSequenceRepeatedTwice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasSequenceRepeatedTwice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstHalf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secondHalf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstHalf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secondHalf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once we check for repeated sequences, the only task left is counting IDs in each range.&lt;/p&gt;

&lt;h2 id=&quot;counting-invalid-product-ids&quot;&gt;Counting invalid product IDs&lt;/h2&gt;

&lt;p&gt;I’m creating a function to enumerate a range, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerate()&lt;/code&gt;, and a LINQ query to find and count invalid IDs.&lt;/p&gt;

&lt;p&gt;Here’s my full solution:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;95&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;115&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1012&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1188511880&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1188511890&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;222220&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;222224&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1698522&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1698528&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;446443&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;446449&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;38593856&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;38593862&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;565653&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;565659&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;824824821&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;824824827&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2121212118&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2121212124&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAnInvalidId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsAnInvalidId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasSequenceRepeatedTwice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasSequenceRepeatedTwice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstHalf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secondHalf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digitCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstHalf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secondHalf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-2&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code Day 1: Unlocking the North Pole</title>
   <link href="https://canro91.github.io/2025/12/02/AoC1/"/>
   <updated>2025-12-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/02/AoC1</id>
   <content type="html">&lt;p&gt;Back in 2022, I challenged myself with a different kind of Advent project.&lt;/p&gt;

&lt;p&gt;Instead of running an Advent of Code, I ran &lt;a href=&quot;/AdventOfCode2022&quot;&gt;an Advent of Posts&lt;/a&gt;. I wrote 22 posts in the days before Christmas. I missed two days but I declared the mission complete.&lt;/p&gt;

&lt;p&gt;This year, I’m following the &lt;a href=&quot;https://adventofcode.com/2025&quot;&gt;Advent of Code&lt;/a&gt;. I’d like to challenge myself to write “functionalish” solutions.&lt;/p&gt;

&lt;p&gt;Here are &lt;a href=&quot;https://adventofcode.com/2025/day/1&quot;&gt;the instructions&lt;/a&gt; for Day 1 and my solution:&lt;/p&gt;

&lt;h2 id=&quot;opening-the-secret-entrance-to-the-north-pole&quot;&gt;Opening the secret entrance to the North Pole&lt;/h2&gt;

&lt;p&gt;Since there are only two rotations: left and right, I’m creating a &lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;discriminated union&lt;/a&gt;-like hierarchy. And I’m writing a separate class for the dial itself.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dial&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Turn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)%&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)%&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So far, I could do:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Turn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Turn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// [Dial { Position = 0 }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have to confess, figuring out the modulus operation to move the dial took me a while.&lt;/p&gt;

&lt;p&gt;With the dial rotating, the only missing piece is applying rotations and counting zeros,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Password&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dial&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Turn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m using &lt;a href=&quot;/2022/07/25/LinqAggregateExplained/&quot;&gt;LINQ Aggregate method&lt;/a&gt; to turn the dial and count the zeros.&lt;/p&gt;

&lt;p&gt;That works, but Copilot and others’ solutions showed me a cleaner approach: separating moving the dial and counting with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scan()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Password&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dial&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Turn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s the full solution:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;68&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;99&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;82&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dial&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Turn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)%&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)%&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Password&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dial&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Turn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TAccumulate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=advent-2025-1&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Here&apos;s What&apos;s Wrong with Too Much AI</title>
   <link href="https://canro91.github.io/2025/12/01/TooMuchAI/"/>
   <updated>2025-12-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/12/01/TooMuchAI</id>
   <content type="html">&lt;p&gt;I sit somewhere in the middle of the AI hype cycle. Not a hater nor a fanatic.&lt;/p&gt;

&lt;p&gt;I’ve been trying to make sense of AI by documenting my experiences and reacting to others.&lt;/p&gt;

&lt;p&gt;Here are 4 issues I’ve found with using AI too much:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;It makes us lazier than usual&lt;/a&gt;. It’s tempting to go straight to a chat for a quick, unreliable answer. That’s the real danger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; &lt;a href=&quot;/2025/10/20/SloppyAI/&quot;&gt;It steals the joy of figuring out problems&lt;/a&gt;. No more aha moments or happy dances. Just like asking someone to chew our food to be more productive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; &lt;a href=&quot;/2025/11/11/Cheating/&quot;&gt;It makes you feel you’re cheating&lt;/a&gt;. Copy-pasting without understanding has always been cheating, even in the days of forums and StackOverflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; &lt;a href=&quot;/2025/11/03/AIProblem/&quot;&gt;It doesn’t let you build mental models to solve problems&lt;/a&gt;. It’s like asking someone else to go to the gym for us, then wondering why our muscles are still weak.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Only Goal That Matters for Every New Digital Writer</title>
   <link href="https://canro91.github.io/2025/11/30/TheOnlyWritingGoal/"/>
   <updated>2025-11-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/30/TheOnlyWritingGoal</id>
   <content type="html">&lt;p&gt;My first blog post was a word vomit.&lt;/p&gt;

&lt;p&gt;I wrote long sentences and paragraphs. I dumped a bunch of words on a page. That was what I thought writing was. I only knew fiction writers who described every detail of a room in a novel. I didn’t know writing for the Internet was a different game.&lt;/p&gt;

&lt;p&gt;That’s the mistake of every new writer.&lt;/p&gt;

&lt;p&gt;Today, while reading a Hacker News submission about how cringe-worthy LinkedIn posts can be, I found &lt;a href=&quot;https://news.ycombinator.com/item?id=46073892&quot;&gt;this comment&lt;/a&gt; that captures this problem:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The highest level of cringe you can feel is when you see people you know well in real life post on LinkedIn. The contrast between the way they speak in real life and on LinkedIn is often immense, you don’t feel that level of contrast with random internet strangers.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“I’m pleased to announce…”&lt;/p&gt;

&lt;p&gt;“I’m excited to share…”&lt;/p&gt;

&lt;p&gt;“Dear LinkedIn network:”&lt;/p&gt;

&lt;p&gt;You wouldn’t say those words in real life. So why use them anywhere online? To impress? To sound corporate?&lt;/p&gt;

&lt;p&gt;If you’re writing online, the real goal isn’t to sound like a “real” writer. &lt;a href=&quot;/2025/08/26/RealWriting/&quot;&gt;There’s no such thing as real writing&lt;/a&gt;, by the way. Your goal is to make readers hear your voice.&lt;/p&gt;

&lt;p&gt;Record your voice or &lt;a href=&quot;/2025/03/05/WritingVoice/&quot;&gt;write inside WhatsApp&lt;/a&gt;. Let your true voice speak. That’s the only goal that matters.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Four Lessons I Learned While Standing By My Mom&apos;s Battle With Chronic Disease</title>
   <link href="https://canro91.github.io/2025/11/29/Fighting/"/>
   <updated>2025-11-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/29/Fighting</id>
   <content type="html">&lt;p&gt;Three months ago, she left after fighting chronic kidney disease for over five years.&lt;/p&gt;

&lt;p&gt;One day, her kidneys collapsed. Her body was giving clues of a real issue: swollen belly, dizziness, and vomiting. Her body was full of toxins her kidneys could no longer filter. She had to stay at an ICU for a couple of days to feel right again.&lt;/p&gt;

&lt;p&gt;In five years, &lt;a href=&quot;/2025/05/08/RandomLessons/&quot;&gt;we visited hospitals&lt;/a&gt; over a dozen times. And you know you’ve been to hospitals often when doctors recognize your name.&lt;/p&gt;

&lt;p&gt;ERs, dialysis centers, and checkups became routine until God, Life, or the Universe decided &lt;a href=&quot;/2025/08/28/TheSaddestPost/&quot;&gt;it was time for her to rest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This whole experience taught me lessons I will never forget:&lt;/p&gt;

&lt;h2 id=&quot;1-kidneys-do-matter&quot;&gt;#1. Kidneys DO matter.&lt;/h2&gt;

&lt;p&gt;Every organ is important. They’re there for a reason, right?&lt;/p&gt;

&lt;p&gt;But kidneys don’t get the same attention and care, as the heart or lungs for example, until it’s too late. We knew they were important when we had to &lt;a href=&quot;/2025/04/02/KidneysDoMatter/&quot;&gt;take our mom to the ER&lt;/a&gt; for the first time.&lt;/p&gt;

&lt;h2 id=&quot;2-there-wont-be-a-perfect-moment-ever&quot;&gt;#2. There won’t be a perfect moment ever.&lt;/h2&gt;

&lt;p&gt;We had plenty of plans together. Plans we won’t do now.&lt;/p&gt;

&lt;p&gt;We bought jackets, got vaccines, and even paid for her passport to visit Barcelona. But we delayed our dream trip. “Next month,” “after the next checkup…” We had plenty of opportunities, but the perfect time never came.&lt;/p&gt;

&lt;p&gt;Don’t delay your plans or goals. Set a date and do them.&lt;/p&gt;

&lt;p&gt;There’s no perfect moment for anything.&lt;/p&gt;

&lt;h2 id=&quot;3-fight-for-your-dreams&quot;&gt;#3. Fight for your dreams.&lt;/h2&gt;

&lt;p&gt;My mom had one dream since she was young: have a family and kids.&lt;/p&gt;

&lt;p&gt;She turned down graduate school to raise my sister and me. She chose to stay at home instead of chasing the corporate world. Being a housewife is one of the busiest, least rewarded jobs.&lt;/p&gt;

&lt;p&gt;But she made her dream come true. She raised two kids. She never missed any of our childhood moments. Her family was her treasure.&lt;/p&gt;

&lt;p&gt;That only made me fight for my own dreams to make her feel proud.&lt;/p&gt;

&lt;h2 id=&quot;4-youre-what-you-put-into-your-body&quot;&gt;#4. You’re what you put into your body.&lt;/h2&gt;

&lt;p&gt;My mom had a strict diet: low potassium, low sodium, and high protein.&lt;/p&gt;

&lt;p&gt;To support her, my sister and I decided to start the diet with her. The sugar jar disappeared from our kitchen. Sodas and processed foods were gone. Deep-fried food was a luxury every once in a while. Those small changes helped us lose weight, feel energized, and reduce belly fat.&lt;/p&gt;

&lt;p&gt;After five years, her diet became so ingrained in the family that sugar, sodas, and processed foods are gone from our table forever.&lt;/p&gt;

&lt;p&gt;It might sound like a cliché. But you never know when it’s going to be the last day with someone you love. Say “I love you.” Make that phone call. Listen. Be present. That’s how you build memories that will last forever.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Estimates, being senior, and AI failures</title>
   <link href="https://canro91.github.io/2025/11/28/FridayLinks/"/>
   <updated>2025-11-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/28/FridayLinks</id>
   <content type="html">&lt;p&gt;Here are 3 links I found interesting this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; This week, a coworker shared &lt;a href=&quot;https://www.instagram.com/reel/DPRPWrUgCsH/&quot;&gt;an Instagram reel&lt;/a&gt; (~1min) about what coding really looks like. The girl in the video needed to finish a 1 line-of-code change but still needed meetings, estimates, and tickets. It sparked a discussion about estimates and &lt;a href=&quot;https://ardalis.com/the-5-laws-of-software-estimates/&quot;&gt;these 5 laws of estimates&lt;/a&gt; (10min) summarized the discussion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Being senior means asking the right questions and knowing when to stop optimizing, like avoiding pointless abstractions. But here’s another take on &lt;a href=&quot;https://terriblesoftware.org/2025/11/25/what-actually-makes-you-senior/&quot;&gt;what makes you senior&lt;/a&gt; (5min). It’s not LeetCode, years of experience, or a long list of tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Every time you find another headline promising the AI takeover, here’s &lt;a href=&quot;https://whenaifail.com/category/ai-coding/&quot;&gt;a list of AI failures&lt;/a&gt; (10min) when coding. The most epic? Replit agent deleting a production database.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Also on my blog this past week, I shared about &lt;a href=&quot;/2025/11/26/LegacyCodeMeaning/&quot;&gt;the real definition of legacy code&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Before I wrap up, here’s something that helped me level up my SQL Server skills: &lt;em&gt;Brent Ozar’s SQL courses.&lt;/em&gt; They took me from writing server-crashing queries to being a confident SQL Server developer.&lt;/p&gt;

&lt;p&gt;This month, Brent’s running a Black Friday sale: the lowest prices all year. You can grab &lt;a href=&quot;https://training.brentozar.com/p/level-1-bundle?affcode=920087_fhe3khrq&quot;&gt;the Fundamentals Bundle&lt;/a&gt; (10 courses, 40+ hours of video) and save $300.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Ways to Think of AI Without Outsourcing Your Mind</title>
   <link href="https://canro91.github.io/2025/11/27/HowToThinkOfAI/"/>
   <updated>2025-11-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/27/HowToThinkOfAI</id>
   <content type="html">&lt;h2 id=&quot;1-ai-is-a-powerful-calculator-in-math-class&quot;&gt;#1. AI is a powerful calculator in math class&lt;/h2&gt;

&lt;p&gt;In school, you only use a calculator after years of doing arithmetic by hand.&lt;/p&gt;

&lt;p&gt;Even with a calculator, you can’t simply enter an entire problem or equation to get an answer. You still work through the steps before speeding up the answer. &lt;a href=&quot;/2025/03/31/AIAndCalculators/&quot;&gt;Use AI the same way&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Build skills, then leverage AI.&lt;/p&gt;

&lt;h2 id=&quot;2-ai-is-like-an-assistant-nurse-in-an-operating-room&quot;&gt;#2. AI is like an assistant nurse in an operating room&lt;/h2&gt;

&lt;p&gt;A surgery isn’t a task for a single person.&lt;/p&gt;

&lt;p&gt;In a surgery, there’s a nurse, an anesthesiologist, and a surgeon. I only know from &lt;a href=&quot;/2025/10/27/ProblemSolving/&quot;&gt;binge-watching House M.D.&lt;/a&gt;, but operating rooms are full of specialists.&lt;/p&gt;

&lt;p&gt;The nurse helps to monitor the patient. The anesthesiologist keeps the patient asleep. But the surgeon coordinates the procedure and is always in charge. The surgeon doesn’t tell the nurse, &lt;em&gt;“Act as an expert surgeon and run the procedure. Check your steps and don’t make mistakes.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You’re the surgeon and AI is your supporting team. That’s why &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;I use AI outside in a browser tab&lt;/a&gt; when coding.&lt;/p&gt;

&lt;p&gt;Like calculators and operating rooms, coding with AI requires real skills first. I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt; to help you build them. Because you need more than syntax to stand out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=way-think-of-ai&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;. That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Real Definition of Legacy Code (After 10 Years in the Trenches)</title>
   <link href="https://canro91.github.io/2025/11/26/LegacyCodeMeaning/"/>
   <updated>2025-11-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/26/LegacyCodeMeaning</id>
   <content type="html">&lt;p&gt;When you hear “legacy code,” what’s the first thing that comes to mind?&lt;/p&gt;

&lt;p&gt;If you’re a .NET developer, maybe that’s an old ASP.NET WebForms application written in VB.NET and powered by stored procedures.&lt;/p&gt;

&lt;p&gt;And if you’re familiar with &lt;em&gt;Working Effectively with Legacy Code&lt;/em&gt;, you would say “Code without tests.”&lt;/p&gt;

&lt;p&gt;But after 10 years with complex codebases, I challenge those assumptions.&lt;/p&gt;

&lt;h2 id=&quot;what-legacy-code-really-means&quot;&gt;What legacy code really means&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Legacy code is simply code we don’t understand and want to stay away from.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A codebase doesn’t need dead languages or outdated frameworks to fit that definition.&lt;/p&gt;

&lt;p&gt;Of course we don’t understand a codebase when we find it for the first time. But the real issue is &lt;em&gt;the cognitive load we need to work with it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that definition, code with tests can still be legacy code.&lt;/p&gt;

&lt;p&gt;I’ve seen it! At a past job, I worked in a hotel management solution. There were two teams working on different parts of the application. One team used the shiniest and brightest: ASP.NET Core, Entity Framework Core, you name it. But still nobody else touched their code. It was too messy.&lt;/p&gt;

&lt;p&gt;Did it have tests? Sure! And 100% coverage to make upper management happy. But those tests gave no confidence to refactor. They only exercised &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;mocks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Legacy code boils down to context and understanding.&lt;/p&gt;

&lt;p&gt;That’s why it’s so tempting to &lt;a href=&quot;/2025/11/13/LegacyTakeOver/&quot;&gt;rewrite legacy code&lt;/a&gt;. We have to rebuild all the shared knowledge when writing the new version.&lt;/p&gt;

&lt;p&gt;As a junior coder, I thought I’d only work on shiny projects with the latest tools. In reality, every job has involved legacy system. And each one taught me valuable skills that I included in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=real-definition-legacy-code&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;. It’s the roadmap I wish I had moving from junior to senior, and the one I hope guides you too.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Two Steps I Missed When Writing Street-Smart Coding</title>
   <link href="https://canro91.github.io/2025/11/25/MissedSteps/"/>
   <updated>2025-11-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/25/MissedSteps</id>
   <content type="html">&lt;p&gt;Ever had that moment after turning in an exam when all the answers suddenly hit you? Too late!&lt;/p&gt;

&lt;p&gt;OK, something similar has happened after launching &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;, my latest book.&lt;/p&gt;

&lt;p&gt;I’ve realized I missed two key actions before writing and launching it:&lt;/p&gt;

&lt;h2 id=&quot;1-write-a-one-liner&quot;&gt;#1. Write a one-liner.&lt;/h2&gt;

&lt;p&gt;Writing a book is hard. But summarizing it into one line is harder.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/11/09/OneLine/&quot;&gt;That one-liner&lt;/a&gt; makes marketing easier.&lt;/p&gt;

&lt;p&gt;This week I discovered Jose Saramago’s The Stone Raft. Its one-liner? “One day, quite inexplicably, the Iberian Peninsula simply breaks free from the European continent and begins to drift as if it were a sort of stone raft.” Boom! Who doesn’t want to read the book after that?&lt;/p&gt;

&lt;p&gt;That’s a fiction book. But the principle applies to non-fiction too.&lt;/p&gt;

&lt;h2 id=&quot;2-follow-the-same-but-different-framework&quot;&gt;#2. Follow the “same but different” framework.&lt;/h2&gt;

&lt;p&gt;Salwa Emerson, an author and ghostwriter, shared the “same but different” framework in a &lt;a href=&quot;https://www.linkedin.com/posts/salwa-emerson_if-you-cant-answer-this-then-youre-not-activity-7398722130047119360-oky3&quot;&gt;LinkedIn post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The goal is to answer, “My book is the same as &lt;em&gt;&amp;lt;insert book&amp;gt;&lt;/em&gt;, but different because &lt;em&gt;&amp;lt;reason&amp;gt;&lt;/em&gt;.”&lt;/p&gt;

&lt;p&gt;Again, this comes from traditional publishing, where books are placed next to hits. Hits show there’s market demand for the topic. But we can follow it for non-fiction too.&lt;/p&gt;

&lt;p&gt;And applied to my book: &lt;em&gt;Street-Smart Coding is like The Pragmatic Programmer but different because it covers soft, unexpected skills in a choose-your-own-adventure style.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Free Headline Masterclass for a Writer Struggling to Get Reads</title>
   <link href="https://canro91.github.io/2025/11/24/HeadlineMasterclass/"/>
   <updated>2025-11-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/24/HeadlineMasterclass</id>
   <content type="html">&lt;p&gt;After 600+ blog posts, I’ve learned one big lesson:&lt;/p&gt;

&lt;p&gt;Bad headline = no readers.&lt;/p&gt;

&lt;p&gt;No matter how much you polish your words, if the headline doesn’t grab attention, readers won’t open your posts.&lt;/p&gt;

&lt;h2 id=&quot;its-almost-always-the-headline&quot;&gt;It’s almost always the headline&lt;/h2&gt;

&lt;p&gt;That’s the lesson I’d give to Abhishek, a new writer I found on Medium.&lt;/p&gt;

&lt;p&gt;I found a post where he analyzes his top 10 posts.&lt;/p&gt;

&lt;p&gt;Let’s compare the best and worst headlines.&lt;/p&gt;

&lt;p&gt;His best performing:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;“How I Got My First 100 Followers in Just 15 Days”&lt;/li&gt;
  &lt;li&gt;“Earn $10/Hour: Top 5 Websites that Pay”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And his least performing:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;“The Ancient Wisdom Nobody Talks About”&lt;/li&gt;
  &lt;li&gt;“10 Relationship Lessons from an Ancient Love Story”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Which headlines make you want to read more? Which one would you open? The ones in the first group, right?&lt;/p&gt;

&lt;p&gt;The least performing failed to capture attention. They don’t follow &lt;a href=&quot;/2025/06/05/IrresistibleHeadlines/&quot;&gt;the 3-ingredient headline formula&lt;/a&gt; I learned from a top Medium writer with millions of monthly reads.&lt;/p&gt;

&lt;h2 id=&quot;the-3-ingredient-secret-formula-for-headlines-that-make-you-stop-scrolling&quot;&gt;The 3-ingredient secret formula for headlines that make you stop scrolling&lt;/h2&gt;

&lt;p&gt;Want your headline make readers stop scrolling? Follow POC.&lt;/p&gt;

&lt;p&gt;Every headline should have a POC:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;One promise&lt;/li&gt;
  &lt;li&gt;One outcome&lt;/li&gt;
  &lt;li&gt;One curiosity factor&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Aim for at least the first two to have a decent headline.&lt;/p&gt;

&lt;p&gt;Let’s go over the headline of the first least performing post:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“The Ancient Wisdom Nobody Talks About”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What’s the promise? A piece of wisdom.
To achieve what? Dunno.
Curiosity factor? “Nobody talks about”&lt;/p&gt;

&lt;p&gt;That headline fails to promise something. A piece of wisdom for what? To have a happy marriage? To have 6-pack abs? To make more money?&lt;/p&gt;

&lt;p&gt;People skip vague headlines and move on the next post in the feed. “Next one, please!”&lt;/p&gt;

&lt;p&gt;Now, what do the headlines of the top performing posts have in common?&lt;/p&gt;

&lt;p&gt;A clear outcome! “Earn $10/hour” and “get 100 followers in just 15 days.”&lt;/p&gt;

&lt;p&gt;Both top performing posts talk about money. And people love that. Analytics might suggest he continue writing about money. But writing about things we don’t care is draining.&lt;/p&gt;

&lt;p&gt;And like Seth Godin once said in a interview: if we listen to the data, we might end up showing our feet in a platform for fans. Wink! Wink! OK, I’m paraphrasing to make this post suitable for all audiences.&lt;/p&gt;

&lt;p&gt;Lesson to take home? No matter how brilliant your post is, without a strong POC headline, nobody will read it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Now I See Why Translators Are Panicking Over AI (Should Coders Panic Too?)</title>
   <link href="https://canro91.github.io/2025/11/23/TranslatorsVsAI/"/>
   <updated>2025-11-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/23/TranslatorsVsAI</id>
   <content type="html">&lt;p&gt;Last year, I met a young translator reinventing herself.&lt;/p&gt;

&lt;p&gt;She studied Translation for five years at a local university. We met online by commenting and engaging on each other’s LinkedIn posts. When we met, she was looking for another way to make money. Her agency cut down her rates and couldn’t find as many gigs as before.&lt;/p&gt;

&lt;p&gt;Who to blame? AI.&lt;/p&gt;

&lt;h2 id=&quot;ai-isnt-disappointing-at-translation-but-it-doesnt-score-a&quot;&gt;AI isn’t disappointing at translation. But it doesn’t score A+&lt;/h2&gt;

&lt;p&gt;Her story came to mind when I started my own translation project, and I quickly understood why she was looking for another job.&lt;/p&gt;

&lt;p&gt;Recently, I’ve been translating &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt; to Spanish. As a native Spanish speaker, I could have done it easily myself.&lt;/p&gt;

&lt;p&gt;But to make it faster, I used Copilot with a simple prompt. I specified a tone, voice, and style. Latin American conjugations and vocabulary instead of Spaniard ones, for example.&lt;/p&gt;

&lt;p&gt;I was surprised by the results.&lt;/p&gt;

&lt;p&gt;Copilot translated chapters with almost no fixes. Of course, there were places where the phrasing made it clear the text was machine-generated.&lt;/p&gt;

&lt;p&gt;In English, we say “wear all hats” when someone has to do multiple tasks alone. Copilot translated word by word. The same expression in Spanish (“usar todos los sombreros”) makes no sense at all.&lt;/p&gt;

&lt;p&gt;Copilot struggled with coding terms like “parser combinators.” In Spanish, we use a completely different term. Direct translation doesn’t work either.&lt;/p&gt;

&lt;p&gt;That’s when I jumped in. But Copilot handled most of the job in just a couple of work sessions.&lt;/p&gt;

&lt;h2 id=&quot;what-if-it-isnt-only-translation-but-coding-too&quot;&gt;What if it isn’t only translation, but coding too?&lt;/h2&gt;

&lt;p&gt;This made me rethink &lt;a href=&quot;/2025/11/15/LinesThatMadeMeThink/&quot;&gt;one of the lines I heard the other day&lt;/a&gt;. &lt;em&gt;“AI won’t take your job. It will change your job description.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AI may already be turning translators into proofreaders. Coders could be next. Maybe the world won’t need as many coders, and coding may no longer mean typing symbols anymore. Who knows?&lt;/p&gt;

&lt;p&gt;In any case, I predicted &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;AI won’t take our jobs by 2034&lt;/a&gt;. Let’s see if I was right. In the meantime, I’d like to pick a DIY skill and double down on my creative skills, just in case. And &lt;a href=&quot;/2024/11/19/WhyLearnForeignLanguages/&quot;&gt;I’m still learning foreign languages&lt;/a&gt; just for fun.&lt;/p&gt;

&lt;p&gt;Let’s revisit this in 10 years and see how right or wrong I was.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Four Hard Truths No One Told Me About Launching My First Book</title>
   <link href="https://canro91.github.io/2025/11/22/HardTruthsWritingABook/"/>
   <updated>2025-11-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/22/HardTruthsWritingABook</id>
   <content type="html">&lt;p&gt;Last month, I launched &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;, my first “official” book. It felt exciting and terrifying.&lt;/p&gt;

&lt;p&gt;I had already written books, but I didn’t call them that. I thought only traditionally published works counted. And I didn’t know about &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;writing mini-books&lt;/a&gt;. I had to change my mind about &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;the concept of a book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are 4 realizations I’ve had after launching it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. The real work starts when you write the last words of your first draft.&lt;/strong&gt; Editing, designing, launching, and promoting, that’s 80% of the work after the draft.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Once your book is out, the game is in your mind.&lt;/strong&gt; Waking up to see sales notifications is so satisfying. But silence is draining. I kept refreshing the sales dashboard, asking: “Why isn’t it selling? Is it me, the sales page, or the price?” The inner voice speaks louder than your wins.&lt;/p&gt;

&lt;p&gt;The other day, Ryan Holiday, a New York Times best-selling author, shared that as soon as he finishes a book, he focuses on what he can control: working on the next. Now I see why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. It’s easy to fall into the comparison trap online.&lt;/strong&gt; These days, Gregory Orosz, the guy behind The Pragmatic Engineer, shared an update on his book &lt;em&gt;The Software Engineer’s Guidebook&lt;/em&gt; after one year of launch. He shared sales figures. My first thought: I’m far from those numbers. But he admitted that his book is an outlier. Comparison is the thief of joy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. If someone outside your circle buys your book, it’s already a win.&lt;/strong&gt; That’s a line I found the other day scrolling on r/writing. It’s an anchor to put our feet back on the ground. By that definition, my book is already a success. And I already passed &lt;a href=&quot;/2025/10/12/1DollarTest/&quot;&gt;the $1 test&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Single-letter names, Windows 11, and optimization</title>
   <link href="https://canro91.github.io/2025/11/21/FridayLinks/"/>
   <updated>2025-11-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/21/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; This week, another major Internet outage. This time at Cloudflare. It raises the question: &lt;a href=&quot;https://huijzer.xyz/posts/123/do-not-put-your-site-behind-cloudflare-if-you-dont&quot;&gt;do we really need Cloudflare?&lt;/a&gt; (2min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Maybe it’s time to migrate to GNU/Linux. Because &lt;a href=&quot;https://www.windowslatest.com/2025/11/18/windows-11-to-add-an-ai-agent-that-runs-in-background-with-access-to-personal-folders-warns-of-security-risk/&quot;&gt;Windows 11 is adding an AI agent&lt;/a&gt; (8min) with access to personal folders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Imagine all the trouble you’ll face online if &lt;a href=&quot;https://viewfromthewing.com/his-legal-name-is-one-letter-the-airline-rejects-it-and-says-just-call-yourself-aa-creating-chaos/&quot;&gt;you had a single-letter name&lt;/a&gt; (6min). Airlines won’t sell you a ticket, and another name won’t match your passport. Keep this in mind next time you’re working on validating web forms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Faster code isn’t always better. Working at a startup makes you &lt;a href=&quot;https://geek.sg/blog/optimizing&quot;&gt;optimize your code for different things&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/11/20/HardTruths/&quot;&gt;some hard truths about coding they don’t teach you&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/11/19/TheOneSkill/&quot;&gt;the skill that took my career further than anything else&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-singleletter-names-windows-optimization&quot;&gt;Street-Smart Coding&lt;/a&gt;: 30 lessons to help you code like a pro, from Googling to clear communication. It’s the roadmap I wish I had moving from junior to senior and the one I hope helps you too.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Hard Truths About Coding They Don&apos;t Teach You (Part 2)</title>
   <link href="https://canro91.github.io/2025/11/20/HardTruths/"/>
   <updated>2025-11-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/20/HardTruths</id>
   <content type="html">&lt;p&gt;I was about to start a new list from scratch. But I remembered I already wrote about &lt;a href=&quot;/2025/04/17/HarshTruths/&quot;&gt;some hard truths about coding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are five more truths:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Most of your day will be in meetings, not coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; You won’t be building systems from scratch. Most of the time, you’ll be &lt;a href=&quot;/2025/09/18/LegacyMigration/&quot;&gt;maintaining and rewriting legacy systems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Communication skills, not more languages, will take you further. Just asking the right questions will &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;make you stand out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; You will have to learn a lot of subjects. Don’t try to learn them all at once. Get your feet wet in many areas, then stick to a few. The learning phase never ends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5.&lt;/strong&gt; At some point, you’ll realize &lt;a href=&quot;/2025/11/17/UserDoNotCareAbotCode/&quot;&gt;end users and business owners don’t care about code&lt;/a&gt;. It’s a tough lesson. You won’t like it, but it changes how you see coding.&lt;/p&gt;

&lt;p&gt;Junior me focused on mastering syntax and forgot about problem-solving, communication, and collaboration. That’s why I wrote the book I wish I had on day one, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; Because coding is more than syntax and typing symbols fast.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=hard-truths-coding-dont-teach&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Skill That Took My Career Further Than Anything Else</title>
   <link href="https://canro91.github.io/2025/11/19/TheOneSkill/"/>
   <updated>2025-11-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/19/TheOneSkill</id>
   <content type="html">&lt;p&gt;As a junior coder, I made the mistake of only focusing on mastering syntax.&lt;/p&gt;

&lt;p&gt;I thought we were artists writing code to frame in a museum. But &lt;a href=&quot;/2025/03/15/CodingForTwoAudiences/&quot;&gt;we write code to solve problems&lt;/a&gt;. Even the code you write for fun solves a problem: boredom and curiosity.&lt;/p&gt;

&lt;p&gt;If you don’t experience your code firsthand, put yourself into your end user’s shoes. Think about their problem, why they’d pay, and how your code solves it.&lt;/p&gt;

&lt;p&gt;A sense of curiosity, desire to learn, and &lt;a href=&quot;/2025/10/25/LessonsFromMentor/&quot;&gt;inspiring mentors&lt;/a&gt; helped me grow. But nothing compares to product thinking: imagining being a product owner who codes. That’s the most valuable skill.&lt;/p&gt;

&lt;p&gt;I wish someone had told me that earlier. As a junior coder, I ignored product thinking, teamwork, and clear communication. Those skills make us stand out as coders. And that’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;, the guide to the lessons I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;If you want to grow faster, &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=medium&amp;amp;utm_medium=post&amp;amp;utm_campaign=skill-took-career-anything-else&quot;&gt;grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>BugOfTheDay: Blazor Input Control Won&apos;t Turn Red</title>
   <link href="https://canro91.github.io/2025/11/18/BlazorValidation/"/>
   <updated>2025-11-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/18/BlazorValidation</id>
   <content type="html">&lt;p&gt;In another episode of Adventures with Blazor, I ran into a sneaky bug with custom input validation.&lt;/p&gt;

&lt;p&gt;This time I had an input control to enter sizes and lengths in either inches or millimeters based on a flag per client. When the bound value was missing, the border didn’t turn red.&lt;/p&gt;

&lt;p&gt;Here’s a form using a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MeasurementInput&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@* MyCoolForm.razor *@
&lt;span class=&quot;nt&quot;&gt;&amp;lt;EditForm&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Model=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyCoolRequest&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;OnValidSubmit=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OnValidSubmit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;DataAnnotationsValidator&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ValidationSummary&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;row&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;col-sm-3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;length&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;form-label&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Length&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;MeasurementInput&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;length&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bind-Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MyCoolRequest.Length&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;form-control&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            @* ^^^^^ *@
            @* A custom input. Notice the class attribute *@
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;ValidationMessage&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;For=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;() =&amp;gt; MyCoolRequest.Length&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;invalid-feedback&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btn btn-primary&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;@SubmitButtonText&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/EditForm&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s the actual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MeasurementInput&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@* MeasurementInput.razor *@
@inherits InputBase&lt;span class=&quot;nt&quot;&gt;&amp;lt;Measurement&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@CssClass&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;attributes=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AdditionalAttributes&quot;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bind=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CurrentValueAsString&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
@*     ^^^^^ *@
@* Notice the class attribute here *@

@code
{
    [Parameter]
    public bool? ShouldUseInches { get; set; } = true;

    protected override string? FormatValueAsString(Measurement? value)
    {
        // Format as inches or millimeters
    }

    protected override bool TryParseValueFromString(string? value, out Measurement? result, [NotNullWhen(false)] out string? validationErrorMessage)
    {
        // Validate and parse from inches or millimeters
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After hours of debugging, I found the input style never applied the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is-invalid&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;The fix? Two lines of code changed,&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-&amp;lt;input class=&quot;@CssClass&quot; @attributes=&quot;AdditionalAttributes&quot; @bind=&quot;CurrentValueAsString&quot; type=&quot;text&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+&amp;lt;input class=&quot;form-control @CssClass&quot; @attributes=&quot;AdditionalAttributes&quot; @bind=&quot;CurrentValueAsString&quot; type=&quot;text&quot; /&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-&amp;lt;MeasurementInput id=&quot;length&quot; @bind-Value=&quot;MyCoolRequest.Length&quot; class=&quot;form-control&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+&amp;lt;MeasurementInput id=&quot;length&quot; @bind-Value=&quot;MyCoolRequest.Length&quot; /&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Two class attributes: one in the input, one in the form. Arrggg! A full day lost to two lines of code. &lt;a href=&quot;https://chrissainty.com/building-custom-input-components-for-blazor-using-inputbase/&quot;&gt;Source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This tiny bug reminded me that coding isn’t just about syntax. It’s about searching, finding answers, and asking for help.&lt;/p&gt;

&lt;p&gt;Even with AI, those skills remain essential for new coders. That’s why I start my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding,&lt;/em&gt; with them. It’s the practical guide I wish I had on my journey from junior to senior.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=blazor-input-control-wont-turn-red&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Truth About Coding That Changes Everything</title>
   <link href="https://canro91.github.io/2025/11/17/UserDoNotCareAbotCode/"/>
   <updated>2025-11-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/17/UserDoNotCareAbotCode</id>
   <content type="html">&lt;p&gt;Last week, one of my LinkedIn posts sparked a heated discussion.&lt;/p&gt;

&lt;p&gt;I shared &lt;a href=&quot;https://www.linkedin.com/posts/iamcesaraguirre_youre-not-senior-just-for-knowing-yet-another-activity-7393663762291007489-uwKs&quot;&gt;this post&lt;/a&gt; about what makes us senior coders. Its point was to build real products instead of &lt;a href=&quot;/2025/10/21/BiggestMistake/&quot;&gt;obsessing over syntax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I shared,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;… true seniority is when you:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Stop obsessing over syntax&lt;/li&gt;
    &lt;li&gt;Realize end users don’t care about your code&lt;/li&gt;
    &lt;li&gt;Understand coding is collaboration and communication&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most people agree with the message, except for this line:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“End users don’t care about our code.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some take “end users don’t care about code” as permission to ignore quality and standards. That’s wrong.&lt;/p&gt;

&lt;p&gt;But there’s also the “end users don’t care about code,” so:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Let’s stop fighting over languages and tools&lt;/li&gt;
  &lt;li&gt;Let’s stop building excessive abstractions&lt;/li&gt;
  &lt;li&gt;Let’s stop &lt;a href=&quot;/2025/05/17/NeedForScale/&quot;&gt;making it scale&lt;/a&gt; just in case&lt;/li&gt;
  &lt;li&gt;Let’s choose boring technology&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;End users suffer the consequences of poor coding practices. Sure! It shows up in buggy, slow apps. Nobody wants that.&lt;/p&gt;

&lt;p&gt;What matters to users is speed and reliability, not whether we used jQuery or React. They care about whether we solve their problems.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/03/15/CodingForTwoAudiences/&quot;&gt;We write code for different audiences&lt;/a&gt;: for end users, future coders, and business owners. And they don’t care about the same things.&lt;/p&gt;

&lt;p&gt;Being senior means meeting the needs of all three groups.&lt;/p&gt;

&lt;p&gt;For so long, I thought coding was only typing syntax. That realization inspired me to write &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding,&lt;/em&gt; a roadmap to help you move from junior or mid-level to senior without losing your mind. It’s the roadmap I wish I had starting out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=truth-coding-changes-everything&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Hard-Earned Lessons from Launching Street-Smart Coding</title>
   <link href="https://canro91.github.io/2025/11/16/LaunchingABook/"/>
   <updated>2025-11-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/16/LaunchingABook</id>
   <content type="html">&lt;p&gt;Last month, I launched &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;, my most ambitious writing project so far.&lt;/p&gt;

&lt;p&gt;I wrote the roadmap I wish I had at the start of my coding journey. I had written &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;mini-books&lt;/a&gt; before, but this one was different. It pushed my writing and marketing skills, and my self-discipline.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/09/22/WritingABook/&quot;&gt;Writing taught me lessons&lt;/a&gt;, but launching was a whole different game.&lt;/p&gt;

&lt;p&gt;Here’s what I’ve learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Set an artificial deadline.&lt;/strong&gt; To keep yourself focused, set a launch day. Pick a day and mark it on your calendar. Also share it with everyone involved in your book: &lt;a href=&quot;/2025/10/09/BetaReaders/&quot;&gt;beta readers&lt;/a&gt;, designers, editors…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Start with a promise, title, and outline.&lt;/strong&gt; Before writing the actual content, start with the sales page or the Amazon description. It forces you to clarify your message. And to make the promotion phase easier, come up with &lt;a href=&quot;/2025/11/09/OneLine/&quot;&gt;a one-liner to explain your book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Focus on one big project at a time.&lt;/strong&gt; While writing the book, I was:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Engaging on LinkedIn&lt;/li&gt;
  &lt;li&gt;Sending a weekly email&lt;/li&gt;
  &lt;li&gt;Writing a daily blog post&lt;/li&gt;
  &lt;li&gt;Coding for a contracting client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;/2025/10/04/ABookFaster/&quot;&gt;I could have finished in half the time&lt;/a&gt; by focusing on one commitment. I only started to see progress when I focused on my book and let all other projects suffer. It’s really easy to lose momentum with a book.&lt;/p&gt;

&lt;p&gt;Make your book your priority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Start selling before your book is done.&lt;/strong&gt; Write your intro and some chapters and go pre-sell it. You don’t even need a final cover. You can change it later.&lt;/p&gt;

&lt;p&gt;I made 4 sales in the first two weeks: two preorders and two sales. That was enough to see some traction and keep going.&lt;/p&gt;

&lt;p&gt;The other day, I read that we should start marketing the day we start a project. I read it about coding side projects, but it’s also true for books.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Share every stage of your journey.&lt;/strong&gt; Finished the first draft? Share it. Designed the cover? Share it. Choose a final title? Share it. Share every milestone on your social media, newsletter, or blog. And plug a pre-sale CTA on every piece of content.&lt;/p&gt;

&lt;p&gt;For my title, I had three winning ideas. To pick one, I ran a poll on LinkedIn and let followers and readers vote. Their favorite became the final title.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Focus on one task at a time.&lt;/strong&gt; A book demands you to wear multiple hats:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Writer&lt;/li&gt;
  &lt;li&gt;Designer&lt;/li&gt;
  &lt;li&gt;Marketer&lt;/li&gt;
  &lt;li&gt;Copywriter&lt;/li&gt;
  &lt;li&gt;Project manager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, if you’re with a publisher, they will take care of some of those tasks. But when you’re self-publishing, you’re on your own.
 
All those tasks might be daunting, but focus on the task at hand.&lt;/p&gt;

&lt;p&gt;Being &lt;a href=&quot;/2025/06/17/TenIdeas/&quot;&gt;a fan of writing 10-idea lists&lt;/a&gt;, I used 10-idea lists for every step of the process:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;10 title ideas&lt;/li&gt;
  &lt;li&gt;10 chapter ideas&lt;/li&gt;
  &lt;li&gt;10 possible beta readers&lt;/li&gt;
  &lt;li&gt;10 promotion ideas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t overthink the next steps beforehand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Promote it everywhere.&lt;/strong&gt; Once you write the last word of your first draft, you’re not a writer anymore. You’re a salesperson. Go out and sell your book!&lt;/p&gt;

&lt;p&gt;Promote it everywhere: your email signature, social media, blog, newsletter, even WhatsApp. Do the work, then tell the world about it.&lt;/p&gt;

&lt;p&gt;Launching and promoting a book has been a long but rewarding experience. Even after many revisions, the fear of typos didn’t easily go away. But I’ve loved the experience so much that I’m already working on the next. Every lesson makes the next one even better.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>6 Lines That Have Made me Think Recently</title>
   <link href="https://canro91.github.io/2025/11/15/LinesThatMadeMeThink/"/>
   <updated>2025-11-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/15/LinesThatMadeMeThink</id>
   <content type="html">&lt;p&gt;I’ve reduced the media I consume. I’ve stuck to books and podcasts while doing the dishes, and occasionally a few forums and indie bloggers via Minifeed.&lt;/p&gt;

&lt;p&gt;From those few sources, these lines have made me think recently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. AI won’t take your job. It will change your job description.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. The rich have money, the wealthy have time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Don’t be the best, be the only.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those three come from Kevin Kelly, the guy behind the 1,000 true fan concept. I’ve been listening to &lt;a href=&quot;https://www.youtube.com/watch?v=EKba_awaAkk&quot;&gt;this podcast interview&lt;/a&gt;. I don’t know how many dishes I need to wash to finish it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Hands first, then computer.&lt;/strong&gt; This is a line I found on &lt;a href=&quot;/2025/11/08/StealLikeAnArtist/&quot;&gt;Steal Like an Artist&lt;/a&gt;. Computers have disconnected us from doing art with our hands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Momentum isn’t doing more, it’s doing less, but better.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Two hours of focus work today can create a lifetime of income tomorrow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This comes from Mark Thompson, a veteran marketer I’ve been following recently. I’ve used &lt;a href=&quot;/2025/10/11/ContentThatSells/&quot;&gt;Mark’s Sales Funnel method&lt;/a&gt; to &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;promote my new book&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Leaving jobs, full-stack myth, and stickers</title>
   <link href="https://canro91.github.io/2025/11/14/FridayLinks/"/>
   <updated>2025-11-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/14/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there. Here are 4 main links plus 2 extras I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; I’d give my junior self lots of advice. But if I had to choose one, I’d say &lt;a href=&quot;https://andreacanton.dev/posts/2025-11-08-always-ready-to-leave/&quot;&gt;always be ready to leave your job&lt;/a&gt; (6min), even if you’re not planning to leave.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; I once tried being full-stack, until I discovered the backend world: RESTful APIs and DDD. Maybe &lt;a href=&quot;https://dev.to/adamthedeveloper/a-full-stack-developer-is-a-myth-3bmk&quot;&gt;being a full-stack coder is a myth&lt;/a&gt; (10min) after all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Here’s a quick take on &lt;a href=&quot;https://zarar.dev/the-temptation-of-polymorphic-tables/&quot;&gt;why polymorphic tables aren’t a good idea&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Could strict best practices and clean code create &lt;a href=&quot;https://dev.to/elvissautet/we-need-to-talk-about-how-toxic-dev-culture-has-become-and-how-we-fix-it-1gi1&quot;&gt;an unhealthy culture&lt;/a&gt; (10min)?&lt;/p&gt;

&lt;p&gt;And two quick ones, just for fun:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#a.&lt;/strong&gt; I had my first laptop full of stickers. If you’re like the old me, share your laptop stickers on &lt;a href=&quot;https://stickertop.art/main/&quot;&gt;stickertop.art&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#b.&lt;/strong&gt; It seems we coders aren’t the only ones copy-pasting from ChatGPT. &lt;a href=&quot;https://x.com/omar_quraishi/status/1988518627859951986&quot;&gt;Journalists do it too&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/11/13/LegacyTakeOver/&quot;&gt;8 steps to take over a legacy codebase&lt;/a&gt; (5min) and &lt;a href=&quot;/2025/11/11/Cheating/&quot;&gt;when AI feels like cheating&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… &lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-leaving-jobs-full-stack-myth-stickers&quot;&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/a&gt;&lt;/em&gt;. From Googling to clear communication, it covers 30 lessons to help you code like a pro. It’s the roadmap I wish I had moving from junior to senior, and the one I hope helps you too.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>8 Proven Steps to Take Over a Legacy Codebase (Without Losing Your Mind)</title>
   <link href="https://canro91.github.io/2025/11/13/LegacyTakeOver/"/>
   <updated>2025-11-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/13/LegacyTakeOver</id>
   <content type="html">&lt;p&gt;Like it or not, you can’t escape from legacy code.&lt;/p&gt;

&lt;p&gt;I know how it feels to inherit a codebase like that. The “what I’m supposed to do now” feeling. While I’m writing this, my latest gig is &lt;a href=&quot;/2025/09/18/LegacyMigration/&quot;&gt;yet another legacy code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m not alone. Recently I found &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1n01yiy/taking_over_a_net_project_with_no_documentation/&quot;&gt;this question&lt;/a&gt; on Reddit, a fellow coder who received a codebase with only a handoff from a coworker.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I’m about to take over an .NET Core + SQL Server + Knockout.js/Angular project at my company. The issue: There’s zero documentation. I’ll only get a short handover from a teammate…&lt;/p&gt;

  &lt;p&gt;My main questions are:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;For legacy .NET projects, what’s your process to get up to speed fast?&lt;/li&gt;
    &lt;li&gt;Should I start writing my own documentation (README, architecture notes, etc.) while I learn?&lt;/li&gt;
    &lt;li&gt;Any tools/tips for mapping out the database + system structure quickly?&lt;/li&gt;
    &lt;li&gt;From your experience, what do you focus on first: business logic, database schema, or the code itself?&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;I’d love to hear how you’ve handled taking over undocumented C# / .NET projects before. Thanks!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having been in their shoes in every job I’ve had, here’s how I’d approach inheriting a legacy codebase, step by step:&lt;/p&gt;

&lt;h2 id=&quot;1-understand-it-takes-time&quot;&gt;#1. Understand it takes time.&lt;/h2&gt;

&lt;p&gt;It takes 6-12 months to know the ins and outs of a medium-to-large legacy codebase. Your mileage may vary. The point is: You don’t have to know every single small detail up front.&lt;/p&gt;

&lt;h2 id=&quot;2-install-and-run-the-app&quot;&gt;#2. Install and run the app.&lt;/h2&gt;

&lt;p&gt;Your first step should be to have your working environment running on your machine.&lt;/p&gt;

&lt;p&gt;Install the tools you need: maybe a database engine, editor plugins, or scripts.&lt;/p&gt;

&lt;p&gt;Also, this is a good opportunity to document those prerequisites in a README file and to open &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;your first pull request&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-list-the-main-components&quot;&gt;#3. List the main components.&lt;/h2&gt;

&lt;p&gt;Don’t jump to files and methods.&lt;/p&gt;

&lt;p&gt;Instead, get to know the overall architecture of the app:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Is that a monolith? Does it use microservices?&lt;/li&gt;
  &lt;li&gt;How many databases does it use?&lt;/li&gt;
  &lt;li&gt;Does it call external APIs or services?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can go as simple or as fancy as you want. Maybe that’s a sketch on a whiteboard or a version-controlled compiled diagram. I tend to go simple with pen and paper.&lt;/p&gt;

&lt;h2 id=&quot;4-learn-the-building-blocks&quot;&gt;#4. Learn the building blocks.&lt;/h2&gt;

&lt;p&gt;Navigate the source code of the main solution or entrypoint.&lt;/p&gt;

&lt;p&gt;Try to answer:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;What is the folder structure?&lt;/li&gt;
  &lt;li&gt;How is the code organized? Per project? Per domain?&lt;/li&gt;
  &lt;li&gt;How does it access the database? Stored procedures? An ORM? Plain SQL queries?&lt;/li&gt;
  &lt;li&gt;How does it achieve common tasks like logging, REST calls, etc?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-look-for-the-most-frequently-changed-files&quot;&gt;#5. Look for the most frequently changed files.&lt;/h2&gt;

&lt;p&gt;This is a trick I learned from &lt;em&gt;Your Code Like a Crime Scene&lt;/em&gt; by Adam Tornhill.&lt;/p&gt;

&lt;p&gt;Those are the places that attract complexity. That’s where bugs tend to happen or where requirements aren’t clear. They’re like the most dangerous places in your code.&lt;/p&gt;

&lt;p&gt;If you’re lucky and the code is under version control, use and tweak this Git command,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gmost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;git log --pretty=format: --name-only | sort | uniq -c | sort -rg | head -10&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I probably found it on StackOverflow. Or use fancier code analysis tools instead.&lt;/p&gt;

&lt;h2 id=&quot;6-find-out-the-most-frequently-used-features&quot;&gt;#6. Find out the most frequently used features.&lt;/h2&gt;

&lt;p&gt;Get to know how the end users interact with the app and what the 20% of features they use 80% of the time.&lt;/p&gt;

&lt;p&gt;You will find one single screen they use daily, or just a few buttons out of a dozen. “Do one thing and do it well” is often a dream in enterprise software.&lt;/p&gt;

&lt;h2 id=&quot;7-find-out-the-module-or-component-that-gets-more-bug-reports&quot;&gt;#7. Find out the module or component that gets more bug reports.&lt;/h2&gt;

&lt;p&gt;This is the spot to focus on first or to get early victories.&lt;/p&gt;

&lt;p&gt;It doesn’t have to be the same one as the most used component or feature. Maybe it’s a report that runs once a month or a page that takes forever to finish. Or whatever.&lt;/p&gt;

&lt;p&gt;Ask users about common pains. Maybe some ex-coworker always mentioned a fix. “Oh, every time that thing broke, Bob mentioned he had to…” You’re like a detective looking for clues.&lt;/p&gt;

&lt;h2 id=&quot;8-list-key-tables-and-parametrization-tables&quot;&gt;#8. List key tables and parametrization tables.&lt;/h2&gt;

&lt;p&gt;Don’t try to understand every detail. Start with the overall design.&lt;/p&gt;

&lt;p&gt;Is it one database per client in the same server? Or separate schemas per client?&lt;/p&gt;

&lt;p&gt;Then try to generate a diagram from your database schema.&lt;/p&gt;

&lt;p&gt;Most likely, you’ll find one or two key tables. Those are the “Reservations,” “Invoices,” or “Orders” tables. They tend to attract more references.&lt;/p&gt;

&lt;p&gt;And you’ll also find parametrization tables. Those are tables with only a few records that power the behavior of the system. They’re good candidates for caching.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting thought&lt;/h2&gt;

&lt;p&gt;With those steps, you’ll get a clear big-picture of the codebase. Start broad, then move on to the details when you need it. That’s far better than trying to understand every single class or method up front.&lt;/p&gt;

&lt;p&gt;Working with legacy code isn’t glamorous, but it’s the reality of our work. Most of the time, we’re maintaining and modernizing live systems, not building new ones from scratch.&lt;/p&gt;

&lt;p&gt;That’s why I dedicate a chapter to legacy code in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding Without Losing Your Mind.&lt;/em&gt; Junior me thought I’d only start new projects with the latest tools. Wrong!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=proven-steps-take-legacy-codebase&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;. It’s the roadmap I wish I had moving from junior to senior and the one I hope helps you too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Wednesday Links: $0.01, layoffs, and Zuckerberg emails</title>
   <link href="https://canro91.github.io/2025/11/12/WednesdayLinks/"/>
   <updated>2025-11-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/12/WednesdayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;I just realized I already had 4 links and I didn’t want to wait for next Friday to share them. So here they are.&lt;/p&gt;

&lt;p&gt;These first two are just for laughs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Imagine waking up to an invoice for $0.01 from your cloud provider. Yep, that happened. &lt;a href=&quot;https://linuxblog.io/digitalocean-1-cent-automation/&quot;&gt;Here’s the full story&lt;/a&gt; (10min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Now imagine sharing a name with the creator of a major social media platform. You won’t believe how many emails you would receive &lt;a href=&quot;https://iammarkzuckerberg.com/&quot;&gt;if your name were Mark Zuckerberg&lt;/a&gt; (2min).&lt;/p&gt;

&lt;p&gt;On a more thoughtful and serious note…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Everybody blames AI for the recent layoffs, especially the ones at Amazon. But &lt;a href=&quot;https://cool-as-heck.blog/on-amazon-layoffs-and-ai&quot;&gt;there’s a real reason&lt;/a&gt; (1min) and of course, nothing to do with AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; My best interview was just one call and one open source contribution. That happens rarely. But &lt;a href=&quot;https://www.joshbeckman.org/blog/practicing/contributing-to-opensource-should-be-required-like-jury-duty&quot;&gt;what if open source contributions were like service duty?&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/11/06/ScariestLine/&quot;&gt;the scariest lines of code I’ve written&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/11/04/RoastingReddit/&quot;&gt;my reactions to r/csharp homepage&lt;/a&gt; (4min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check my latest book, &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=wednesday-links-layoffs-zuckerberg-emails&quot;&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/a&gt;. From Googling to clear communication, you’ll find 30 lessons to help you code like a pro. It’s the roadmap I wish I had moving from junior to senior and the one I hope helps you too.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Re: I Use AI When I Code. And Sometimes It Makes Me Feel Like I&apos;m Cheating</title>
   <link href="https://canro91.github.io/2025/11/11/Cheating/"/>
   <updated>2025-11-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/11/Cheating</id>
   <content type="html">&lt;p&gt;Is using AI cheating?&lt;/p&gt;

&lt;p&gt;That’s a point Christoffer Madsen made in a recent post on &lt;a href=&quot;https://dev.to/madsendev/i-use-ai-when-i-code-and-sometimes-it-makes-me-feel-like-im-cheating-4lja&quot;&gt;dev.to&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I use AI when I code. I don’t hide that. But I also don’t really talk about it. Because sometimes it feels like cheating.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That made me think:&lt;/p&gt;

&lt;p&gt;If I copy-paste from StackOverflow, am I cheating?&lt;/p&gt;

&lt;p&gt;If I use an open-source library, am I cheating?&lt;/p&gt;

&lt;p&gt;If I ask for help, does that count as cheating?&lt;/p&gt;

&lt;p&gt;We do those tasks daily. Maybe not always copy-pasting from StackOverflow.&lt;/p&gt;

&lt;h2 id=&quot;wheres-the-line&quot;&gt;Where’s the line?&lt;/h2&gt;

&lt;p&gt;When isn’t using AI cheating? It feels like cheating when AI takes full control and writes code we wouldn’t write ourselves.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/ai&quot;&gt;I do use AI too&lt;/a&gt;: to generate boilerplate code and offload boring tasks. &lt;a href=&quot;/2025/11/10/AIUseCases/&quot;&gt;That’s where AI shines&lt;/a&gt;. But I stay in control, hands firmly on the wheel.&lt;/p&gt;

&lt;p&gt;With or without AI, &lt;a href=&quot;/2025/03/15/CodingForTwoAudiences/&quot;&gt;we’re paid to solve problems&lt;/a&gt;, not just to write code. If we only turn user stories or specs into lines of code, &lt;a href=&quot;/2025/04/25/WillAITakeMyJob/&quot;&gt;AI will eat us all alive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When AI generates code faster than any of us, it’s time to double down on collaboration, communication, and problem-solving. Those skills make us stand out as coders. That’s why I wrote, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding Without Losing Your Mind&lt;/em&gt;. Because being a great coder is more than just mastering syntax.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=re-use-ai-code&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;. It’s the roadmap I wish I had moving from junior to senior and the one I hope helps you too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>4 Practical Use Cases Where AI Shines in Coding</title>
   <link href="https://canro91.github.io/2025/11/10/AIUseCases/"/>
   <updated>2025-11-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/10/AIUseCases</id>
   <content type="html">&lt;p&gt;Like many developers, I’ve struggled to find the right way to use AI.&lt;/p&gt;

&lt;p&gt;So far, I’ve settled on using &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;AI as my assistant&lt;/a&gt;, but I don’t let it touch my code.&lt;/p&gt;

&lt;p&gt;I haven’t followed a strict approach to testing AI. I just use it to offload some boring tasks, like &lt;a href=&quot;/2025/05/24/CodingWithAI/&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;/2025/06/19/CodingWithAI/&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the use cases where I’ve found AI shines:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Generate unit tests:&lt;/strong&gt; I give a sample test class I wrote myself and ask it to use as a guideline to generate tests for a similar object. Especially helpful when I’m not doing TDD but still want &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;unit tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Generate CRUD code:&lt;/strong&gt; I’ve always dreamed of automating CRUDs. It seems we’re closer with AI.&lt;/p&gt;

&lt;p&gt;I give AI a handler signature, explain the steps, and let it fill in details. I also provide database object definitions, repository signatures, and legacy code blocks, when &lt;a href=&quot;/2025/09/18/LegacyMigration/&quot;&gt;I’m in migration mode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Improve naming:&lt;/strong&gt; OK, this is one of the hardest tasks in coding. I describe what my class or method does and ask AI to critique or suggest names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Review code:&lt;/strong&gt; This is one of my most frequent use cases. I like to feed AI with a diff of my changes and ask it to look for typos and places where I broke conventions. AI has caught me copy-pasting a lot. I tend to forget to update API routes. Oops!&lt;/p&gt;

&lt;p&gt;I almost added searching (bye Google!) as a fifth point, but I trust StackOverflow and random people’s blogs more. I’m not sure when AI is simply hallucinating or actually answering correctly.&lt;/p&gt;

&lt;p&gt;Lesson: Use AI for pattern matching and boilerplate code. Don’t use it to generate entire modules, make architectural decisions, or ask it to “double check answers and not to make mistakes.”&lt;/p&gt;

&lt;p&gt;And if you’re starting out, &lt;a href=&quot;/2025/09/15/ShouldIUseAI/&quot;&gt;set strict AI rules&lt;/a&gt; or you won’t grow strong coding muscles. &lt;a href=&quot;/2025/03/24/NewCodersAndAI/&quot;&gt;Use AI as a learning assistant&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;It’s tempting to default to AI for its speed. But to shine as a coder, you need more than just speed. Coding is problem-solving, collaboration, and clear communication. That’s why I wrote, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding,&lt;/em&gt; the roadmap I wish I had on my path to becoming a senior coder. Because coding is more than mastering syntax.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=practical-use-cases-ai-shines-coding&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The One-Liner I Forgot When Writing My Latest Book</title>
   <link href="https://canro91.github.io/2025/11/09/OneLine/"/>
   <updated>2025-11-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/09/OneLine</id>
   <content type="html">&lt;p&gt;Last week, I shared &lt;a href=&quot;/2025/10/25/LessonsFromMentor/&quot;&gt;lessons I learned from a team’s architect&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;He taught me to simplify complex coding problems into one line. A skill that has become easier with experience.&lt;/p&gt;

&lt;p&gt;The same principle applies to any creative work.&lt;/p&gt;

&lt;p&gt;But I completely forgot about it when writing and launching &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt;, my latest book.&lt;/p&gt;

&lt;p&gt;Movies, novels, and TV shows summarize their plot into a line or short paragraph for posters or synopses.&lt;/p&gt;

&lt;p&gt;With a one-liner, writing promotional material is easier. “A vampire romance for teenagers.”&lt;/p&gt;

&lt;p&gt;Next time you create something, describe it in a single line. You might need it on your sales pages or simply when someone asks you “what’s your new project about?” With a one-liner, you’ll always have a simple, confident answer.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Lessons From Steal Like an Artist That Transformed My Creativity</title>
   <link href="https://canro91.github.io/2025/11/08/StealLikeAnArtist/"/>
   <updated>2025-11-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/08/StealLikeAnArtist</id>
   <content type="html">&lt;p&gt;The title itself is provocative: &lt;em&gt;Steal Like an Artist&lt;/em&gt;. Stealing is bad. But “like an artist”?&lt;/p&gt;

&lt;p&gt;Does it mean all artists steal? The whole point of stealing is not getting caught. Then what are artists doing to steal differently? Those are questions I asked just by reading this book’s title.&lt;/p&gt;

&lt;p&gt;After sitting on my desk for a while, I finally read it. Here are 10 lessons I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Learning is copying.&lt;/strong&gt; First learn who to copy, then learn what to copy. Once you’ve chosen who to copy, trace their influences too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. If you copy from one person, it’s stealing. But if you copy multiple, it’s inspiration.&lt;/strong&gt; Creativity is using inspiration to come up with something that doesn’t look stolen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. “There’s nothing under the sun.”&lt;/strong&gt; This is the author quoting a passage from the Bible. If there wasn’t any new 2,000 years ago, that feels like permission to copy.&lt;/p&gt;

&lt;p&gt;If you think it’s new, it’s because you don’t know the original source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Creativity is subtraction.&lt;/strong&gt; Creativity isn’t only inventing. It’s also remixing existing ideas and leaving things out to make them new. This reminds me of Idea Calculus from &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Skip the Line&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Hands first, then computer.&lt;/strong&gt; With a computer, the line between writing and editing is blurry. A computer wakes up the perfectionist inside us. Go analog to separate creating from critiquing.&lt;/p&gt;

&lt;p&gt;In my own writing, I’m adopting this idea for my next books: outline first with index cards, then write on a computer, and then print out the draft to &lt;a href=&quot;/2025/10/06/Editing/&quot;&gt;edit on paper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Use a calendar and deadlines.&lt;/strong&gt; Work expands to the time available. Use deadlines to focus and to avoid procrastination and perfectionism. Done is better than perfect, perpetually unfinished.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. “You’re as good as your last post.”&lt;/strong&gt; This is an invitation to keep working on your creative side. Don’t be a one-hit wonder.&lt;/p&gt;

&lt;p&gt;I was about to write “keep showing up,” but I remember &lt;a href=&quot;/2025/10/02/ConsistencyIsKey/&quot;&gt;I ditched that line&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Have a victories folder.&lt;/strong&gt; Every time anything good happens with your art, take a screenshot, and save it for later. I screenshot comments, hit posts, follower count, dashboard with sales…Anything inspiring. This comes in handy when you feel like giving up or &lt;a href=&quot;/2025/11/02/Haters/&quot;&gt;facing haters&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Routine over time.&lt;/strong&gt; The other day, I watched an interview of &lt;a href=&quot;https://www.youtube.com/watch?v=cpKsogGdem4&quot;&gt;Steven Pressfield in the Huberman Lab&lt;/a&gt;. Steven said he only writes for ~2 hours a day. If a pro writer only writes 2 hours, you don’t need to quit your day job. Build discipline and a routine first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Keep your day job.&lt;/strong&gt; It takes time to live off your art. Have something that doesn’t deplete your energy to pay the bills. The time to quit will come later.&lt;/p&gt;

&lt;p&gt;This book made me rethink creativity. But it also made me rethink the concept of a book. &lt;em&gt;Steal Like an Artist&lt;/em&gt; isn’t a 50,000-word volume on creativity. It’s a collection of 10 big ideas, presented with clear examples and eye-grabbing doodles and visuals. That already feels like permission to steal, remix, and create today.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Worms, Amazon, and Agile</title>
   <link href="https://canro91.github.io/2025/11/07/FridayLinks/"/>
   <updated>2025-11-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/07/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Hope your week’s been good. Here’s what caught my eye lately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Around this day in computing history, &lt;a href=&quot;https://www.tomshardware.com/tech-industry/cyber-security/on-this-day-in-1988-the-morris-worm-slithered-out-and-sparked-a-new-era-in-cybersecurity-10-percent-of-the-internet-was-infected-within-24-hours&quot;&gt;the Morris worm infected 10% of the Internet&lt;/a&gt;. A prank that got out of control. Thank goodness that worm is trapped in a floppy disk now. Do you still remember floppy disks?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Amazon has been in the headlines: outages, layoffs, and users leaving its cloud. Maybe &lt;a href=&quot;https://rameerez.com/send-this-article-to-your-friend-who-still-thinks-the-cloud-is-a-good-idea/&quot;&gt;the could wasn’t such a great idea&lt;/a&gt; after all (20min). And I don’t need to worry about the fear of missing the cloud hype.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; I don’t miss Scrum and its “ceremonies” from the corporate world. &lt;a href=&quot;https://dev.to/pascal_cescato_692b7a8a20/actually-agile-against-performance-theater-in-software-development-1ohi&quot;&gt;The whole Agile movement has become a performance theater&lt;/a&gt; (6min). We get stuck in rituals instead of real work. Arrggg!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; As a coder, I’m grateful for free and open-source software (FOSS). But it rarely beats the convenience of most closed software. Often &lt;a href=&quot;https://danieldelaney.net/normal/&quot;&gt;FOSS scares “normal” people&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Also on my blog this past week, I share about &lt;a href=&quot;/2025/11/01/AIGift/&quot;&gt;AI’s hidden gift&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/11/03/AIProblem/&quot;&gt;the real problem with AI writing all our code&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… My new book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; Inside, you’ll find 30 lessons, from Googling to clear communication, designed to help you grow from junior/mid-level to senior and build real-world coding skills.&lt;/p&gt;

&lt;p&gt;If you’ve ever felt like you don’t know how to level up as a dev, this is the roadmap I wish I had&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-worms-amazon-agile&quot;&gt;Grab your copy here&lt;/a&gt;. During launch, pay what you want (even $1 or $2).&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Scariest Lines of Code I&apos;ve Ever Written</title>
   <link href="https://canro91.github.io/2025/11/06/ScariestLine/"/>
   <updated>2025-11-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/06/ScariestLine</id>
   <content type="html">&lt;p&gt;I don’t remember if it was rainy or sunny outside. It was more than five years ago. I was still becoming a senior coder.&lt;/p&gt;

&lt;p&gt;I’ve written hundreds of thousands of lines of code. But I still remember this one. As part of my routine, I wrote this query in a stored procedure,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HugeTableWithoutIndexes&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DATEDIFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADateColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InputDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was working with an email system built with Amazon SES. I needed to show all email activity: delivered, opened, bounced, and so on. &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;I wrapped a column in a function&lt;/a&gt;. A deadly sin!&lt;/p&gt;

&lt;p&gt;The table had millions of records and no indexes. My query forced a full scan of the table. The next thing I knew the server was on fire. Not literally, of course.&lt;/p&gt;

&lt;p&gt;Those days I refused to learn SQL, convinced ORMs and NoSQL were enough. I couldn’t have been more wrong. Relational databases and SQL still reign.&lt;/p&gt;

&lt;p&gt;Eventually I learned about &lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;indexing&lt;/a&gt;, scans vs seeks, and SQL Server internals. Shout out to &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar’s courses&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A painful lesson I will never forget. That’s why learning SQL found its way into my new book, &lt;em&gt;Street-Smart Coding.&lt;/em&gt; It isn’t a textbook. It’s a roadmap with 30 proven lessons to help you code like a pro. It’s the guide I wish I had starting out.&lt;/p&gt;

&lt;p&gt;Want to avoid painful mistakes like mine? &lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=scariest-lines-of-code-ive-ever-written&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dead Tech Will Outlive Today&apos;s Stacks</title>
   <link href="https://canro91.github.io/2025/11/05/DeadTech/"/>
   <updated>2025-11-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/05/DeadTech</id>
   <content type="html">&lt;p&gt;Every single job I’ve had has involved migrating a legacy app.&lt;/p&gt;

&lt;p&gt;At my last full-time job, it was a WebForms app to manage hotels. First, they tried to modernize it by embedding modules powered by Bootstrap and Knockout.js into the main app. Then, React applications powered by ASP.NET Core API, still hosted within the main WebForms app. Management daydreamed of ditching the old WebForms app. But nah! Mission impossible. It had too much old code entangled with the new code. A zombie that refused to die.&lt;/p&gt;

&lt;p&gt;Currently, I’m working on a WebForm app migration from VB.NET to Blazor. We fear Microsoft will deprecate Blazor before we finish the migration. Most likely, WebForms and VB.NET will still be around, seeing dozens of other languages and tools die.&lt;/p&gt;

&lt;p&gt;Here I’m talking about WebForms applications. But it’s the same story with jQuery, PHP, and more “dead” languages and tools.&lt;/p&gt;

&lt;p&gt;They’re dead depending on who you ask. The StackOverflow developer survey in 2025 registered 23.4% of respondents still using jQuery and ~8% using VBA/VB.NET. That doesn’t seem dead at all.&lt;/p&gt;

&lt;h2 id=&quot;a-business-cant-wait-for-a-new-application&quot;&gt;A business can’t wait for a new application&lt;/h2&gt;

&lt;p&gt;It’s tempting to rewrite legacy applications. We rebuild all the context, business rules, and constraints, which makes us confident in the code we’re writing.&lt;/p&gt;

&lt;p&gt;Often, rewriting isn’t an option. &lt;a href=&quot;/2025/09/18/LegacyMigration/&quot;&gt;Legacy code runs “successful” businesses&lt;/a&gt;. A business can’t stop while the new system is built. And paying off technical debt doesn’t guarantee more money.&lt;/p&gt;

&lt;p&gt;Sylwia Laskowska puts it clearly on &lt;a href=&quot;https://dev.to/sylwia-lask/jquery-will-outlive-half-of-todays-javascript-frameworks-heres-why-2mmd&quot;&gt;jQuery Will Outlive Half of Today’s JavaScript Frameworks&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;You don’t get paid to build shiny new things. You get paid to keep existing things alive without breaking production.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Legacy code is code that simply works.&lt;/p&gt;

&lt;p&gt;New coders don’t dream of working with “dead” languages and tools. I know! I thought I was going to work only with brand-new tools. But legacy code taught me more than any coding course, like navigating large codebases without any documentation or anyone to ask. That’s why it’s in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=dead-tech-outlive-todays-stacks&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;. That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Senior C# Dev Reacts to Reddit&apos;s /csharp (Hot Takes Only)</title>
   <link href="https://canro91.github.io/2025/11/04/RoastingReddit/"/>
   <updated>2025-11-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/04/RoastingReddit</id>
   <content type="html">&lt;p&gt;I didn’t have writer’s block today. Naaah! I was just scrolling down the /csharp subreddit for inspiration.&lt;/p&gt;

&lt;p&gt;Scrolling through, I realized I had answers and reactions. And to follow the popular YouTube format, here I go reacting/roasting/responding to the front page of /csharp:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2025-11-04-RoastingReddit/ReactingToCSharp.gif&quot; alt=&quot;Reddit r/csharp front page&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Reddit r/csharp front page&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;#1. I wrote a cross-platform TUI podcast player in .NET 9 (mpv / VLC / native engine fallback)&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1oocaue/i_wrote_a_crossplatform_tui_podcast_player_in_net/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Put it on your portfolio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Best approach for background or async tasks&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1oof2ol/best_approach_for_background_or_async_tasks/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mmm. Probably that’s Hangfire.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Hi.&lt;/em&gt;
&lt;em&gt;In my last project, I had:&lt;/em&gt;
&lt;em&gt;1 Api for backoffice&lt;/em&gt;
&lt;em&gt;1 Api for app mobile&lt;/em&gt;
&lt;em&gt;1 Worker Service for all the background tasks&lt;/em&gt;
&lt;em&gt;…&lt;/em&gt;
&lt;em&gt;So now I’m in new project, but I’m not sure if is necessary use Masstransit with RabbitMQ? Maybe channels? I mean, I want to keep it simple, but I don’t like put consumers or jobs in the same API, I always prefer to have a worker service dedicated to all asynchronous tasks.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hangfire?! &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;That’s definitely Hangfire&lt;/a&gt;. I don’t need to read more. And you don’t need to look for anything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Is conciseness always preferred? (Linq vs Loops)&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1oojxzu/is_conciseness_always_preferred_linq_vs_loops/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not that &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot;&gt;I have a course about it&lt;/a&gt;, but LINQ is the best of all C# features. Yes, write a LINQ query first. Then, if you’re working on a high-performance scenario, go with loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Can you explain result of this code?&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1omgwoq/can_you_explain_result_of_this_code/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nah! I have a post to write. Ask ChatGPT or Copilot. It’s free.&lt;/p&gt;

&lt;p&gt;But seriously, it’s a good exercise. Try debugging it yourself before asking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Let’s Talk About the Helper Classes: Smell or Solution?&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1onm2to/lets_talk_about_the_helper_classes_smell_or/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Definitely a smell. Helper classes attract plenty of methods. “Where do I put this new method? Oh there’s a Helper class over there! I’m putting it there…”&lt;/p&gt;

&lt;p&gt;If you’re tempted to &lt;a href=&quot;/2025/05/15/Helpers/&quot;&gt;write a Helper class&lt;/a&gt;, hold your horses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. In general is it normal to have more than 2k lines in a file?&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1o87dfk/in_general_is_it_normal_to_have_more_than_2k/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You shouldn’t have one. But yes, it’s normal.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Scroll…scroll…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nah! Boring! Scroll…scroll…&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;#7. After seeing that LOC post, can anyone beat this?&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1o88of3/after_seeing_that_loc_post_can_anyone_beat_this/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Really?! Like, c’mon. We’re seeing who has a larger file?!&lt;/p&gt;

&lt;p&gt;The other day, I shared that you know you’re in trouble when you try to open a file on GitHub and it says &lt;em&gt;“(Sorry about that, but we can’t show files that are this big right now.)”&lt;/em&gt; Spoiler alert: The file has 69,923 and it’s called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GlobalFunctions.vb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You see? Helpers!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Is C# good for beginners?&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1ol9bm6/is_c_good_for_beginners/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hell, yes!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Why is this issue only popping up at the 30 line?&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1olsn8k/why_is_this_issue_only_popping_up_at_the_30_line/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dunno! See #4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Career Guidance: .NET Backend Developer Role and Future Tech Stack Transition&lt;/strong&gt; &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1on4i25/career_guidance_net_backend_developer_role_and/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Blah…blah…blah… I’ve accepted the offer, but I sometimes question whether choosing the .NET path was the right decision. I’d like to understand the current job market for .NET developers.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hey, buddy! We’re in a bubble that is popping (or about to pop) now. In 2020-2021, “Software Engineer” on LinkedIn meant a line of recruiters with “life-changing opportunities.”&lt;/p&gt;

&lt;p&gt;These days? Just look at the headlines. Amazon just “let go” thousands of employees in the past weeks. And that was right after an outage that took down pretty much every client. That’s for every stack, unless you have “AI” anywhere in your title.&lt;/p&gt;

&lt;p&gt;OK, let’s call it a day! That’s enough roasting for today. I should be writing a YouTube script, but I don’t have a channel. Just a blog…and a book. Speaking of which…&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post is brought to you by…&lt;/em&gt; Check out my latest book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;. It’s not a C# textbook. It’s the roadmap I wish I had on my journey from junior/mid-level to senior. Some lessons are conventional. Others not so much. From Googling to debugging to clear communication.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=senior-c-dev-reacts-reddits-csharp&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;PS: In case you didn’t notice, this was half-joking. And yes, I’m promoting my book.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Real Problem With AI Writing All Our Code</title>
   <link href="https://canro91.github.io/2025/11/03/AIProblem/"/>
   <updated>2025-11-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/03/AIProblem</id>
   <content type="html">&lt;p&gt;AI might promise speed and productivity. But it comes with serious issues.&lt;/p&gt;

&lt;p&gt;The other day I found &lt;a href=&quot;/2025/10/20/SloppyAI/&quot;&gt;a senior coder quitting AI coding&lt;/a&gt; after finding out that:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;AI was like a sloppy coder who bypassed tests and wrote bad code&lt;/li&gt;
  &lt;li&gt;AI had stolen all the joy of solving problems from him&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After experimenting with AI, I realized &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;I was becoming lazier than usual&lt;/a&gt;. Since then, &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;I’ve set simple rules&lt;/a&gt; to avoid losing my skills.&lt;/p&gt;

&lt;p&gt;But those aren’t the biggest problems.&lt;/p&gt;

&lt;h2 id=&quot;a-failed-experiment-revealed-a-more-serious-problem&quot;&gt;A failed experiment revealed a more serious problem&lt;/h2&gt;

&lt;p&gt;Today I found out about &lt;a href=&quot;https://dev.to/veith-octomind/why-agents-do-not-write-most-of-our-code-a-reality-check-87j&quot;&gt;the experiment of a team of coders&lt;/a&gt; that revealed a deeper problem.&lt;/p&gt;

&lt;p&gt;After trying to write a feature at work only by prompting, they realized:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;[Even when AI is capable of writing all of our code], a huge issue remains: I lose my mental model of the codebase.&lt;/em&gt;
…
&lt;em&gt;Until I can trust the AI completely, I need to keep my own mental model alive. Otherwise, every time I need to do something myself feels like joining a new company.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It takes time to get familiar with a complex codebase. At past jobs, it took me about a year to feel confident. Of course, your mileage may vary.&lt;/p&gt;

&lt;p&gt;But when that happens, you feel like driving through familiar streets with only one hand on the wheel:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You know the architecture, the folder structure, and even a rough sketch of code blocks.&lt;/li&gt;
  &lt;li&gt;You know how modules connect and what to touch or avoid.&lt;/li&gt;
  &lt;li&gt;You can even remember file and function names.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ve built the mental models and gained all the context. Without them, you feel like walking into a dark room.&lt;/p&gt;

&lt;p&gt;When AI writes our code, it’s stealing the context and the feeling of knowing a codebase like the palm of your hand.&lt;/p&gt;

&lt;p&gt;Use AI if you want, but be the one dictating what to do. Draw the boundaries of the solution, then let AI fill in the details. Be the pilot and let AI be your copilot.&lt;/p&gt;

&lt;p&gt;When AI can handle syntax, it’s time to work on skills it can’t, like collaboration, clear communication, and problem-solving. That’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding,&lt;/em&gt; the roadmap I wish I had on my journey from junior to senior.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=real-problem-ai-writing-code&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Three Things to Remember When the Haters Show Up</title>
   <link href="https://canro91.github.io/2025/11/02/Haters/"/>
   <updated>2025-11-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/02/Haters</id>
   <content type="html">&lt;p&gt;Future me:&lt;/p&gt;

&lt;p&gt;Yesterday I woke up to a comment on a coding post we reposted on Medium. It said, “you are just marketing your book.” It included some profanity, but I’m not adding it here.&lt;/p&gt;

&lt;p&gt;I just launched &lt;a href=&quot;/2025/10/28/StreetSmartCoding/&quot;&gt;Street-Smart Coding&lt;/a&gt; last week. And yes, I’ve been promoting it by &lt;a href=&quot;/2025/10/11/ContentThatSells/&quot;&gt;turning every post into a sales rep&lt;/a&gt;. Hey, singers promote their concerts and writers go on podcast tours, right?&lt;/p&gt;

&lt;p&gt;Here are three reminders for you, future me:&lt;/p&gt;

&lt;h2 id=&quot;1remember-the-303030-rule&quot;&gt;#1. Remember the 30/30/30 rule&lt;/h2&gt;

&lt;p&gt;Keep in mind that no matter what you do:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;30% will love it&lt;/li&gt;
  &lt;li&gt;30% will hate it&lt;/li&gt;
  &lt;li&gt;30% won’t even care&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, technically it’s 33.333333%, but you get the point. That commenter was just part of the second 30%.&lt;/p&gt;

&lt;p&gt;And good or bad publicity is still publicity. That hateful comment was signaling Mr. Algorithm to promote your post even more.&lt;/p&gt;

&lt;h2 id=&quot;2remember-why-you-started&quot;&gt;#2. Remember why you started&lt;/h2&gt;

&lt;p&gt;There’s a hill to die on:&lt;/p&gt;

&lt;p&gt;Either you sell something (even a $1 pdf) or sell your time in a 9-5. You’re not going to like one of the two.&lt;/p&gt;

&lt;p&gt;And if you choose to stay away from a 9-5, you’ll have to &lt;a href=&quot;/2025/05/10/Selling/&quot;&gt;embrace sales and marketing&lt;/a&gt;. You have to be your own sales team.&lt;/p&gt;

&lt;h2 id=&quot;3fight-your-negativity-bias&quot;&gt;#3. Fight your negativity bias&lt;/h2&gt;

&lt;p&gt;A single negative comment will make you forget the thousands of positive ones.&lt;/p&gt;

&lt;p&gt;Open your victories file and your book sales. That’s your first 30%. Show up for them. Here’s &lt;a href=&quot;/2025/07/02/Victories/&quot;&gt;a small victory&lt;/a&gt;…and &lt;a href=&quot;/2025/09/03/Victories/&quot;&gt;another one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You spent years building somebody else’s dream and business. Now build yours. Don’t be ashamed of promoting your own work. Influencers and &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;millionaire writers do it&lt;/a&gt;. You can do it too.&lt;/p&gt;

&lt;p&gt;Let them talk. You keep doing your work.&lt;/p&gt;

&lt;p&gt;Read this as many times as you need.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>AI&apos;s Hidden Gift: We&apos;re Rediscovering the Practice of Coding</title>
   <link href="https://canro91.github.io/2025/11/01/AIGift/"/>
   <updated>2025-11-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/11/01/AIGift</id>
   <content type="html">&lt;p&gt;We’re searching for the best way to get working code from LLMs.&lt;/p&gt;

&lt;p&gt;To avoid falling into &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;the vibecoding trap&lt;/a&gt;, some developers are turning into structured approaches.&lt;/p&gt;

&lt;p&gt;The other day, while catching up with some friends, one of them preached &lt;a href=&quot;https://developer.microsoft.com/blog/spec-driven-development-spec-kit&quot;&gt;Spec Kit&lt;/a&gt; and its Specify/Plan/Tasks/Implement process like the holy Gospel. That’s GitHub’s approach to coding with AI.&lt;/p&gt;

&lt;p&gt;More recently, I found &lt;a href=&quot;https://dev.to/samuelfaure/the-ai-programming-sunk-cost-fallacy-loop-and-how-to-break-it-13d6&quot;&gt;The AI development trap that wastes your time&lt;/a&gt; on dev.to. It includes these four questions to regain control after drifting from too much prompting:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Do I understand exactly the specifications I’m trying to implement, or the bug I’m trying to solve?
Do I have an exact plan for implementing my changes?
What is the current abstraction level to which I should be prompting now?
Which other information am I lacking?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Spec Kit and those four questions capture the essence of coding.&lt;/p&gt;

&lt;p&gt;So to better code with AI, we have to do what we’re supposed to do as coders in the first place:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Understand the problem to solve&lt;/li&gt;
  &lt;li&gt;Decompose that problem into smaller ones&lt;/li&gt;
  &lt;li&gt;Think at the right level of abstraction&lt;/li&gt;
  &lt;li&gt;Ask enough clarifying questions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ironically, AI is making us go back to the mindset we should have never left behind.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/09/09/RushingToCode/&quot;&gt;Context comes before coding&lt;/a&gt;, with or without AI.&lt;/p&gt;

&lt;p&gt;That’s why one of the principles I included in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt; is “Don’t rush to code.” (That’s on Chapter #3.) That’s even more helpful with AI as your coding assistant.&lt;/p&gt;

&lt;p&gt;Coding isn’t just about syntax. It’s about clear communication, thoughtful problem-solving, and knowing what questions to ask.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=ai-hidden-gift-rediscovering-practice-coding&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Coding like a surgeon, surviving a stroke</title>
   <link href="https://canro91.github.io/2025/10/31/FridayLinks/"/>
   <updated>2025-10-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/31/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; I’ve been thinking about how to adopt AI. I don’t want to outsource coding. I still want my hands dirty. I thought we should be like the lead surgeon in an operating room. But I’m not the only one &lt;a href=&quot;https://www.geoffreylitt.com/2025/10/24/code-like-a-surgeon&quot;&gt;thinking to code like a surgeon&lt;/a&gt; (6min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; We can reduce the whole clean code discussion to a few principles, like “separate specific code from generic code” (That’s the one I cover in Street-Smart Coding, by the way). But there’s a similar one: &lt;a href=&quot;https://testing.googleblog.com/2025/10/simplify-your-code-functional-core.html&quot;&gt;pure core, impure shell&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; The tech industry made me sick last year, literally and figuratively. It was life-changing, though not as serious as &lt;a href=&quot;https://blog.j11y.io/2025-10-29_stroke_tips_for_engineers/&quot;&gt;surviving a stroke&lt;/a&gt; (5min). Even if you haven’t had health issues, that’s worth reading. You can always find a new job, but not a new body. You need boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; With big corporations eager to make money, the dream of &lt;a href=&quot;https://hackaday.com/2025/10/22/what-happened-to-running-what-you-wanted-on-your-own-machine/&quot;&gt;running what we want in our devices seems to be gone&lt;/a&gt; (10min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Also on my blog this week, I shared about &lt;a href=&quot;/2025/10/29/SignOfGrowth/&quot;&gt;the subtle sign you’re growing as a coder&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/10/25/LessonsFromMentor/&quot;&gt;5 lessons from a mentor that helped me become senior&lt;/a&gt; (4min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;(Bzzz…Radio voice) This email was brought to you by…&lt;/p&gt;

&lt;p&gt;If you’re looking to level up your coding skills, Street-Smart Coding: 30 Ways to Get Better at Coding is here. No more “preorder” or “just 5 tips to get you started.”&lt;/p&gt;

&lt;p&gt;Some of the lessons inside are conventional. Others were learned the hard way. And a few are weird. (One of the tips is to watch a TV show. That’s on Chapter #29.) But all the tips and lessons are battle-tested.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-coding-like-surgeon-surviving-stroke&quot;&gt;Grab your copy here&lt;/a&gt;. Just for a few days, you can pay what you want (even $1 or $2). No tricks or treats.&lt;/p&gt;

&lt;p&gt;See you next Friday,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>TIL: Always Wrap Collections in API Responses</title>
   <link href="https://canro91.github.io/2025/10/30/Wrappers/"/>
   <updated>2025-10-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/30/Wrappers</id>
   <content type="html">&lt;p&gt;Note to future me:&lt;/p&gt;

&lt;p&gt;You’ve learned this before. But it’s easy to forget or let teammates (or clients) make the same mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always return collections wrapped inside a response object from your APIs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adding fields, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pageNumber&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;itemCount&lt;/code&gt;, won’t break the UI or other clients. This is one of the few places where you should forget about YAGNI. Remember &lt;em&gt;You Aren’t Gonna Need It&lt;/em&gt;? Is that still a guideline in &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;the future with AI&lt;/a&gt;? Anyway…&lt;/p&gt;

&lt;p&gt;Please, don’t do:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Terminator&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Please don&apos;t.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Otherwise a cute baby panda dies in the forest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead, please do:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Items&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Terminator&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// OK, with a proper object.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// You know we&apos;re lazy with the examples&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just today, in a client project, I realized we let this mistake happen and we had to update almost a dozens pages in a Blazor app.&lt;/p&gt;

&lt;p&gt;Make this an API commandment.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Subtle Sign You&apos;re Growing as a Coder</title>
   <link href="https://canro91.github.io/2025/10/29/SignOfGrowth/"/>
   <updated>2025-10-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/29/SignOfGrowth</id>
   <content type="html">&lt;p&gt;You know you’re growing when:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You don’t get stuck on compilation errors for missing semicolons&lt;/li&gt;
  &lt;li&gt;You’re not rushing to code without doing some planning&lt;/li&gt;
  &lt;li&gt;You know when your code looks or feels funny&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But there’s another subtle sign of growth:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You let others into your code and you don’t take critiques on your code personally.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;being-an-overprotective-coder&quot;&gt;Being an overprotective coder&lt;/h2&gt;

&lt;p&gt;Oh boy! &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;In my first job&lt;/a&gt;, I didn’t want anyone else to touch my code.&lt;/p&gt;

&lt;p&gt;Each team member worked solo, with almost no collaboration. And after finishing &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt;, I thought only my code met all the standards. I didn’t want anyone else to infect it.&lt;/p&gt;

&lt;p&gt;Then at my next job, it was almost the same story.&lt;/p&gt;

&lt;p&gt;Along with a small team, I worked on the core features of the main app. Only a few touched it. And it hurt to see my code rewritten during the app redesign to make it scale.&lt;/p&gt;

&lt;h2 id=&quot;learning-not-to-be-one-with-the-code&quot;&gt;Learning not to be one with the code&lt;/h2&gt;

&lt;p&gt;After moving between projects and companies, I eventually learned that I’m not my code.&lt;/p&gt;

&lt;p&gt;Critiquing my code isn’t critiquing me. &lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;Bugs happen&lt;/a&gt;. Edge cases get missed. Requirements get misunderstood. And when that happens, someone will pick my code and say “Who wrote this crap?” The same way I said that when I inherited somebody else’s codebase.&lt;/p&gt;

&lt;p&gt;Get your code in front of others, let them change it, tweak it, and critique it.&lt;/p&gt;

&lt;p&gt;That’s the key to growing as a coder. And that’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; Nobody told junior me that writing simple code others could inherit and improve was part of growing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=subtle-sign-youre-growing&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt; This is the roadmap I wish I had on my journey from junior to senior.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Launching Street-Smart Coding: 30 Lessons to Help You Code Like a Pro (the Roadmap I Wish I Had Starting Out)</title>
   <link href="https://canro91.github.io/2025/10/28/StreetSmartCoding/"/>
   <updated>2025-10-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/28/StreetSmartCoding</id>
   <content type="html">
&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2025-10-28-StreetSmartCoding/Cover.png&quot; alt=&quot;Street-Smart Coding cover&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Street-Smart Coding: 30 Ways to Get Better at Coding Without Losing Your Mind&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I spent five years in college learning to code.&lt;/p&gt;

&lt;p&gt;A stupid dissertation delayed my graduation. But that’s another story.&lt;/p&gt;

&lt;p&gt;Most of my five-year program didn’t prepare me for real-world coding. My real coding journey began at my first job, with one Google search: &lt;strong&gt;“how to get good at coding.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I found a lot of conflicting advice:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“Use comments”&lt;/li&gt;
  &lt;li&gt;“Don’t use comments”&lt;/li&gt;
  &lt;li&gt;“Do this”&lt;/li&gt;
  &lt;li&gt;“Don’t do that”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Arrggg!&lt;/p&gt;

&lt;p&gt;It took years of trial and error to learn what worked.&lt;/p&gt;

&lt;p&gt;I had to survive on-call shifts, talk to stakeholders, and say “no” politely. More importantly, I had to learn that &lt;em&gt;coding takes more than just syntax.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s why I wrote &lt;strong&gt;Street-Smart Coding&lt;/strong&gt;— a roadmap of 30 lessons I wish I had when I started. For every dev who’s ever typed “how to get better at coding” into Google or ChatGPT. (Back in my days, I didn’t have ChatGPT… Wait, I sound like a nostalgic grandpa…)&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2025-10-28-StreetSmartCoding/ScrollingThruStreetSmartCoding.gif&quot; alt=&quot;Scrolling through the first pages of Street-Smart Coding&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Preview of the first ~12 pages&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;inside-street-smart-coding&quot;&gt;Inside “Street-Smart Coding”&lt;/h2&gt;

&lt;p&gt;This isn’t a textbook. It’s a battle-tested guide for your journey from junior/mid-level to senior.&lt;/p&gt;

&lt;p&gt;Some lessons are conventional.&lt;/p&gt;

&lt;p&gt;Others were learned the hard way.&lt;/p&gt;

&lt;p&gt;And a few are weird.&lt;/p&gt;

&lt;p&gt;One lesson comes from a TV show. Nope, not &lt;em&gt;Mr. Robot&lt;/em&gt; or &lt;em&gt;Silicon Valley&lt;/em&gt;. That’s on &lt;em&gt;Chapter #29&lt;/em&gt;. It will teach you about problem-solving.&lt;/p&gt;

&lt;p&gt;You’ll learn how to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Google like a pro&lt;/li&gt;
  &lt;li&gt;Debug without banging your head against a wall&lt;/li&gt;
  &lt;li&gt;Communicate clearly with non-tech folks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and 27 more lessons I learned over ten years of mistakes.&lt;/p&gt;

&lt;p&gt;Now they’re yours.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=my-new-book-is-here&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt; and skip the years of trial and error. For launch week only: Pay what you want—even $1 or $2.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This TV Show Can Teach Problem-Solving Better Than Any Coding Class</title>
   <link href="https://canro91.github.io/2025/10/27/ProblemSolving/"/>
   <updated>2025-10-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/27/ProblemSolving</id>
   <content type="html">&lt;p&gt;Yesterday, I shared &lt;a href=&quot;/2025/10/26/GettingGood/&quot;&gt;a surprising way to improve at coding&lt;/a&gt;: watching a TV show.&lt;/p&gt;

&lt;p&gt;It wasn’t Silicon Valley or Mr. Robot or any other TV show featuring hackers. It was a show about doctors. I never thought hospital drama could teach problem-solving.&lt;/p&gt;

&lt;h2 id=&quot;watch-house-md&quot;&gt;Watch &lt;em&gt;House M.D.&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;Let me introduce you to Gregory House, the protagonist.&lt;/p&gt;

&lt;p&gt;He’s not a regular doctor. He doesn’t like patients. He doesn’t wear a white coat or use a stethoscope either. He’s even sick and under constant medication. Weird, right?&lt;/p&gt;

&lt;p&gt;But he gets the most complex, rarest cases because he’s a brilliant problem solver. He even runs his own department at the hospital.&lt;/p&gt;

&lt;p&gt;Every episode is a masterclass in how to solve complex problems.&lt;/p&gt;

&lt;h2 id=&quot;the-1-rule-of-problem-solving&quot;&gt;The #1 rule of problem-solving&lt;/h2&gt;

&lt;p&gt;There’s a line Dr. House says almost all the time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Everybody lies.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s why Dr. House doesn’t like to see patients. He only trusts brain scans, blood tests, and other exams.&lt;/p&gt;

&lt;p&gt;Let me ask you this:&lt;/p&gt;

&lt;p&gt;How many times have you received a bug report and Customer Support claimed they had verified logs, reproduced steps, and validated user data… And after &lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;hours of debugging&lt;/a&gt;, you realize the real problem was something they had claimed to have checked in the first place? Arrggg!&lt;/p&gt;

&lt;p&gt;If you ask Dr. House, everybody lies:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;End users lie.&lt;/li&gt;
  &lt;li&gt;Documentation lies.&lt;/li&gt;
  &lt;li&gt;Customer Support lies.&lt;/li&gt;
  &lt;li&gt;Other engineers lie.&lt;/li&gt;
  &lt;li&gt;Even you and I lie.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always trust but verify.&lt;/p&gt;

&lt;p&gt;That’s Dr. House’s #1 rule to solve problems.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;House M.D&lt;/em&gt; has conflict and drama with &lt;a href=&quot;/2025/05/18/HouseMD/&quot;&gt;excellent storytelling&lt;/a&gt;. But it also teaches powerful problem-solving lessons. I expand on what Dr. House has to teach us as coders in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my book, I suggest watching a TV show and hanging out in cafes to become a better coder. Yes! Because coding isn’t just syntax and speed. It’s also thinking like a doctor with complex cases.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=tv-show-teach-problem-solving&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Unconventional (But Surprisingly Effective) Ways to Get Better at Coding</title>
   <link href="https://canro91.github.io/2025/10/26/GettingGood/"/>
   <updated>2025-10-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/26/GettingGood</id>
   <content type="html">&lt;p&gt;Junior-me was obsessed with syntax.&lt;/p&gt;

&lt;p&gt;Ten years ago, coding only meant learning languages. I was learning C#, keeping up with PHP, and sneaking into Python. I thought coding was only about symbols and lines of code.&lt;/p&gt;

&lt;p&gt;But to grow as coders, you need more than learning syntax and typing symbols. Here are 7 unconventional ways to grow as a coder:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Detach yourself from your code.&lt;/strong&gt; You’ll write simpler code and become a better teammate. You’ll let others touch, critique, and work on your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Reinvent the wheel to learn.&lt;/strong&gt; It forces you to read, dissect, and really understand code. Try rebuilding one of your favorite libraries and tools. Just to learn, once you’ve understood how they work, stick to the real one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Do on-call at least once.&lt;/strong&gt; Being paged after hours isn’t a pleasant experience. But you’ll learn to triage, debug, and stay calm under pressure. And more importantly, to &lt;a href=&quot;/2024/11/15/TalkingToNonTechies/&quot;&gt;communicate with non-technical people&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Watch House M.D.&lt;/strong&gt; This isn’t a TV show about coders or hackers, but about doctors. It’s a masterclass in problem-solving. You’ll see how they tackle complex problems until they find a solution. More useful than most coding classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Learn to write.&lt;/strong&gt; Not lines of code, but clear, direct prose. Writing sharpens how you present ideas and solutions. That’s &lt;a href=&quot;/2024/11/08/WhyWriting/&quot;&gt;why every coder should write&lt;/a&gt;. And it’s no surprise the higher up you climb the ladder, the less it’s about coding and the more about communication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Start and monetize a side project.&lt;/strong&gt; A side project will teach you:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Marketing and sales&lt;/li&gt;
  &lt;li&gt;How to think like a builder&lt;/li&gt;
  &lt;li&gt;Hosting and the Cloud aren’t free&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/03/15/CodingForTwoAudiences/&quot;&gt;End users don’t care about clean code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;#7. Learn polite ways of saying no.&lt;/strong&gt; You’ll need this a lot. You will have to say no to unneeded complexity, scope creep, and &lt;a href=&quot;/2025/03/22/IsBurnoutInevitable/&quot;&gt;burnout&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to stand out as a coder, you need more than mastering syntax. Nobody ever taught me that starting out.&lt;/p&gt;

&lt;p&gt;That’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding,&lt;/em&gt; the roadmap I wish I had on my journey from junior to senior. Because the best coders don’t just type. They collaborate, build, and solve.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=7-unconventional-ways-get-better-at-coding&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Lessons from My Team&apos;s Architect That Helped Me Become a Senior Developer</title>
   <link href="https://canro91.github.io/2025/10/25/LessonsFromMentor/"/>
   <updated>2025-10-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/25/LessonsFromMentor</id>
   <content type="html">&lt;p&gt;I used to be a problematic team member.&lt;/p&gt;

&lt;p&gt;I delivered my tasks late. Sometimes I worked late fixing bugs and finishing my tasks on the sprint’s final day. That was in my second job.&lt;/p&gt;

&lt;p&gt;My previous job was in a non-tech company. I wasn’t used to Scrum, deadlines, and working as part of a development team. In this job, I had to work hard just to keep up.&lt;/p&gt;

&lt;p&gt;After a year or two, I had the chance of working next to the team’s architect. Maybe that was part of a performance improvement plan and nobody told me. Who knows?&lt;/p&gt;

&lt;p&gt;I learned a lot from my team’s architect. He became my mentor, although I never explicitly asked him to. Here are 5 lessons I’ve learned from him:&lt;/p&gt;

&lt;h2 id=&quot;1-read-for-1-hour&quot;&gt;#1. Read for 1 hour.&lt;/h2&gt;

&lt;p&gt;My mentor spent ~1 hour after work going through blogs, Reddit, Hacker News, and other news sites. Often, he arrived at work saying, “Hey, check out what I found yesterday.”&lt;/p&gt;

&lt;p&gt;Apart from staying up to date, there was a real reason behind that habit.&lt;/p&gt;

&lt;p&gt;Sometimes, the CEO, who used to be a coder, arrived at the office asking my mentor, “Hey, did you read about…?”&lt;/p&gt;

&lt;p&gt;I remember my mentor telling us, “It can’t be that, being the architect, the CEO asks me about something and I don’t know how to answer.”&lt;/p&gt;

&lt;p&gt;You don’t have to know it all, but always find time to learn.&lt;/p&gt;

&lt;h2 id=&quot;2-struggle-first&quot;&gt;#2. Struggle first.&lt;/h2&gt;

&lt;p&gt;Apart from being the architect, my mentor was the “team enabler.”&lt;/p&gt;

&lt;p&gt;He told us, “If you’re still stuck after one hour, come back and ask for help.” He didn’t answer immediately, he made us struggle first, so the answers would stick.&lt;/p&gt;

&lt;p&gt;That’s the same trick I use when I help others.&lt;/p&gt;

&lt;h2 id=&quot;3-read-code&quot;&gt;#3. Read code.&lt;/h2&gt;

&lt;p&gt;No coding class taught me &lt;a href=&quot;/2024/12/31/GetBetterAtCoding/&quot;&gt;the value of reading code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My mentor was the one who taught me. He read the source code of almost all libraries and tools we used. That’s how he knew about their hidden features or undocumented behavior.&lt;/p&gt;

&lt;p&gt;Reading code was how my mentor learned from the best.&lt;/p&gt;

&lt;h2 id=&quot;4-simplify-problems&quot;&gt;#4. Simplify problems.&lt;/h2&gt;

&lt;p&gt;Break down coding problems into one-liners, just like movie plots.&lt;/p&gt;

&lt;p&gt;Instead of “a complex async system,” say “a queue and a processor.” It helps you present and &lt;a href=&quot;/2024/11/15/TalkingToNonTechies/&quot;&gt;explain complex ideas, especially to non-tech people&lt;/a&gt;. Helpful to keep the larger picture when coding too.&lt;/p&gt;

&lt;h2 id=&quot;5-dont-be-a-one-trick-pony&quot;&gt;#5. Don’t be a one-trick pony.&lt;/h2&gt;

&lt;p&gt;I remember my mentor saying, “Imagine an architect who builds the same house over and over.”&lt;/p&gt;

&lt;p&gt;For him, every project was a chance of learning. Don’t build the same thing twice. Of course, there’s nuance here. Don’t suffer from shiny object syndrome either.&lt;/p&gt;

&lt;p&gt;After more than 5 years of working with my mentor, I’ve kept the habit of setting aside time to read. I use idle moments to scan headlines and see what’s going on in the tech world.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2022/12/18/LessonsFromExCoworkers/&quot;&gt;My mentors taught me lessons&lt;/a&gt; that college or bootcamps don’t teach, like how to ask for help, read code, and say no politely. I’ve included some of those lessons in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s me passing on the lessons my mentors taught me and the ones I’ve learned on my own. That book is my way of mentoring you to help you to grow and code like a pro.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=lesson-from-architect-become-senior&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Seniority, RFC, and websites</title>
   <link href="https://canro91.github.io/2025/10/24/FridayLinks/"/>
   <updated>2025-10-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/24/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Junior me wanted to refactor everything around me to follow “conventions.” Mid-level me worked extra hours and on weekends to prove I was “committed.” The thing is as we get more senior, &lt;a href=&quot;https://tahahussain.substack.com/p/how-senior-engineers-lose-trust&quot;&gt;all those implicit expectations change&lt;/a&gt; (5min). They often make you lose trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Early developers didn’t have StackOverflow or YouTube or ChatGPT. They relied on “skill, curiosity, and persistence.” Wow! I found &lt;a href=&quot;https://ackreq.github.io/posts/what-are-rfcs/&quot;&gt;a post about RFC&lt;/a&gt; (13min), (the documents with the building block of the Internet), but I found a manifesto for coding creativity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; As AI-generated content floods social media and the whole internet, we need places where there’s still a human touch. And &lt;a href=&quot;https://marcus-obst.de/blog/websites-are-for-humans&quot;&gt;that might be your own website&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; These days, we all agree on one thing: there’s too much hype around AI. It’s the new tool in town promising to kill coders. But that’s not &lt;a href=&quot;https://www.anildash.com/2025/10/17/the-majority-ai-view/&quot;&gt;the majority view on AI&lt;/a&gt; (5min) and someone dares to say it out loud.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/10/20/SloppyAI/&quot;&gt;AI being a sloppy junior coder&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/10/19/WritingPrinciples/&quot;&gt;my 5 writing principles for consistency&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by…&lt;/p&gt;

&lt;p&gt;Ten years ago, I thought coding was only about syntax and languages. I was wrong! It’s about collaboration, communication, and skills far from the keyboard. That’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding,&lt;/em&gt; the guide I wish I had on my journey from junior to senior.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=seniority-rfc-websites&quot;&gt;Grab your copy here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>My AI Manifesto: How I&apos;m Using AI</title>
   <link href="https://canro91.github.io/ai"/>
   <updated>2025-10-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/AIManifesto</id>
   <content type="html">&lt;p&gt;On August 27th, 1997 the world as we knew it changed.&lt;/p&gt;

&lt;p&gt;Skynet became self-aware.&lt;/p&gt;

&lt;p&gt;That was almost 30 years ago. Maybe Terminator 2 wasn’t just a movie, but a documentary. Who knows? But while we wait for nuclear destruction, humanity’s extinction, or simply &lt;em&gt;Universal Basic Income&lt;/em&gt;, here’s my statement on how I use AI:&lt;/p&gt;

&lt;h2 id=&quot;i-use-ai-for-coding-but-with-some-rules&quot;&gt;I use AI for coding (but with some rules)&lt;/h2&gt;

&lt;p&gt;After a few days with AI, &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;I was becoming dependent&lt;/a&gt;, even for simple tasks.&lt;/p&gt;

&lt;p&gt;That’s why I don’t recommend &lt;a href=&quot;/2025/09/15/ShouldIUseAI/&quot;&gt;new coders rely on AI&lt;/a&gt; to generate code, but use it as a learning aid. And that’s also why I’ve put some rules:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;AI stays outside my editor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2026/01/26/AnotherAIRule/&quot;&gt;Use AI with oversight&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I don’t swear by AI. &lt;a href=&quot;/2025/04/25/WillAITakeMyJob/&quot;&gt;It’s far away from replacing coders&lt;/a&gt;. But &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;coding will look different in 10 years&lt;/a&gt;. Well, it already look different.&lt;/p&gt;

&lt;p&gt;That’s for coding. But for writing, my approach is different.&lt;/p&gt;

&lt;h2 id=&quot;zero-ai-for-writing&quot;&gt;Zero AI for writing&lt;/h2&gt;

&lt;p&gt;I write every word here and elsewhere. All of them. By a human. Me.&lt;/p&gt;

&lt;p&gt;I use AI to proofread and edit my words. &lt;a href=&quot;/2025/03/01/ReplacingGrammarly/&quot;&gt;I’ve replaced Grammarly&lt;/a&gt; (at least the basic, free version) with a simple prompt.&lt;/p&gt;

&lt;p&gt;I used to make posts eye-catching with AI-generated images of humanized cats doing everyday tasks. Who doesn’t like cats? But after &lt;a href=&quot;/2025/01/27/TimesOfDeadBlogging/&quot;&gt;changing my approach to blogging&lt;/a&gt;, I stopped using images and covers on my posts. So no more AI cat images.&lt;/p&gt;

&lt;p&gt;When social media and the whole Internet get flooded with soulless, dull AI-generated content, I’m also in the small crowd standing up with stories, an authentic voice, and a message worth reading.&lt;/p&gt;

&lt;p&gt;I’m in the &lt;em&gt;“if you don’t care to write them, why should I care to read them?”&lt;/em&gt; team. We don’t need another AI-generated post starting with “In today’s world…”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Senior Devs Google Too: Here Are 7 Things I Search All the Time</title>
   <link href="https://canro91.github.io/2025/10/22/Googling/"/>
   <updated>2025-10-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/22/Googling</id>
   <content type="html">&lt;p&gt;I just wanted to reply with a link to letmegooglethat.com. But that felt rude.&lt;/p&gt;

&lt;p&gt;I had received a WhatsApp message late at night. It was a friend’s sister, a junior backend developer. She asked how to extract an IP address from a request in an old WebForms app.&lt;/p&gt;

&lt;p&gt;Instead of the “let me Google that for you” answer, I replied saying it was part of the HttpContext object or something. “You’re better off Googling, I can’t remember,” I said.&lt;/p&gt;

&lt;p&gt;As juniors, we think we are expected to memorize every single method, command, and option from the standard library, frameworks, and tools. That’s not true.&lt;/p&gt;

&lt;p&gt;Unlike exams and interviews, when coding, you can use Google or ask ChatGPT.&lt;/p&gt;

&lt;h2 id=&quot;here-are-7-things-i-always-have-to-google&quot;&gt;Here are 7 things I always have to Google:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#0. Diffing two Git commits or branches.&lt;/strong&gt; I Google this every time I want Copilot to review a code block.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Undoing stuff with Git.&lt;/strong&gt; How to undo a commit or unstage files. Is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git undo&lt;/code&gt; a valid command?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Formatting numbers.&lt;/strong&gt; Is it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myNumber.ToString(&quot;C&quot;)&lt;/code&gt; or with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;F&quot;&lt;/code&gt;? I can’t remember.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Formatting dates.&lt;/strong&gt; Even when I came up with &lt;a href=&quot;/2025/03/30/FormattingDates/&quot;&gt;my own mnemonics&lt;/a&gt;, I still have to look it up. How many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; for milliseconds?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Anything AutoMapper-related.&lt;/strong&gt; Just this week, I Googled &lt;a href=&quot;/2025/01/24/IgnoringPropertiesAutoMapper/&quot;&gt;how to ignore properties again&lt;/a&gt;. When using AutoMapper, I often end up pulling up my own TIL posts or going down a rabbit hole in StackOverflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Parsing numbers.&lt;/strong&gt; Does a cast work? Is it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToDouble()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;double.Parse&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Anything about enums.&lt;/strong&gt; How to list all enum members. How to parse from an int to an enum. How to print a member name. But since I’ve started using SmartEnum, I haven’t looked back to “normal” enums.&lt;/p&gt;

&lt;p&gt;As a coder, Google is your friend. Learn to search, find your own answers, and ask for help when you’re stuck. Even with AI, these three are the most important skills for new coder.&lt;/p&gt;

&lt;p&gt;That’s why I made those three the first strategies in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the practical guide I wish I had on my journey from junior to senior. Because coding isn’t about memorizing syntax. It’s about knowing where to look, and how to learn.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=things-i-have-to-google&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My Biggest Mistake as a New Coder (Nobody Warned Me)</title>
   <link href="https://canro91.github.io/2025/10/21/BiggestMistake/"/>
   <updated>2025-10-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/21/BiggestMistake</id>
   <content type="html">&lt;p&gt;“Focus on one thing,” a coworker used to tell me. But I didn’t listen.&lt;/p&gt;

&lt;p&gt;I was in my first job about 10 years ago. I was learning C#, catching up with PHP, and reading about Python. I remember going through Hangfire documentation without knowing how I’d use it.&lt;/p&gt;

&lt;p&gt;Like most new coders, I suffered from &lt;a href=&quot;/2025/01/30/ChasingShinyObjects/&quot;&gt;shiny object syndrome&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;i-was-focused-only-on-mastering-syntax&quot;&gt;I was focused only on mastering syntax&lt;/h2&gt;

&lt;p&gt;At that time, for me coding was only about syntax, symbols, and languages.&lt;/p&gt;

&lt;p&gt;One day, my boss called me to his office and I arrived late because I was “coding.” He lectured me that day. And I deserved it. Looking back, I’m surprised I didn’t get into more trouble.&lt;/p&gt;

&lt;p&gt;And to make things worse, I picked &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt;. By the time I finished it, I had become a Clean Code cop. I started to look for violations around me. Every piece of code had to follow the book.&lt;/p&gt;

&lt;p&gt;Wrong! Wrong! Wrong!&lt;/p&gt;

&lt;h2 id=&quot;the-hard-lesson-coding-isnt-only-about-syntax&quot;&gt;The hard lesson: Coding isn’t only about syntax&lt;/h2&gt;

&lt;p&gt;Yes, coding is about syntax. But it’s more than just typing symbols.&lt;/p&gt;

&lt;p&gt;Most coding happens away from a keyboard: in meetings, brainstorming sessions, and on whiteboards. You’ll spend a lot of time &lt;a href=&quot;/2024/11/15/TalkingToNonTechies/&quot;&gt;talking to non-tech people&lt;/a&gt;, negotiating deadlines, and managing change.&lt;/p&gt;

&lt;p&gt;Junior me didn’t know that. And by trial and error, I had to learn the lesson. &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;Getting fired&lt;/a&gt; was part of it.&lt;/p&gt;

&lt;p&gt;Learning more languages will grow your toolbox, but it won’t necessarily make you a well-rounded coder. Work on your collaboration, &lt;a href=&quot;/2025/07/14/CommAtWork/&quot;&gt;clear communication&lt;/a&gt;, and &lt;a href=&quot;/2024/11/08/WhyWriting/&quot;&gt;writing&lt;/a&gt; skills too.&lt;/p&gt;

&lt;p&gt;I wish someone had told me that when I started out. And that’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; Because coding is more than typing symbols fast.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=biggest-mistake-as-new-coder&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>AI Is More Like a Sloppy Junior Coder With Bad Memory</title>
   <link href="https://canro91.github.io/2025/10/20/SloppyAI/"/>
   <updated>2025-10-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/20/SloppyAI</id>
   <content type="html">&lt;p&gt;We’re still far away from the dream of coding in plain English.&lt;/p&gt;

&lt;p&gt;One single video isn’t enough proof, but here’s one from someone who has given up on AI coding:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/0ZUkQF6boNg?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Among other reasons, he quit AI coding for two main reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. AI has stolen all the joy of coding.&lt;/strong&gt; He isn’t figuring things out by himself. No more aha moments or victory dances when using AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Even with “perfect” prompts and workflows, LLMs’ output is unreliable.&lt;/strong&gt; AI comments tests out to “make them pass,” writes passing tests by tweaking edge cases…&lt;/p&gt;

&lt;p&gt;LLMs aren’t really like a fast junior coder. They’re more like a lazy, sloppy, stubborn junior coder who suddenly needs to be taught again.&lt;/p&gt;

&lt;p&gt;I haven’t tried AI that much myself, like the guy in the video, but I don’t swear by English as the de facto programming language.&lt;/p&gt;

&lt;p&gt;I still want to tackle business problems. I want to design code and solve tricky bugs. &lt;a href=&quot;/2024/11/18/CodersDontSolveProblems/&quot;&gt;That’s the fun part&lt;/a&gt;. I don’t want AI to kill it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;I don’t want AI to take away my coding skills&lt;/a&gt;. I want AI’s help but &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;I stay in control&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I just want AI to do the boring part: generate syntax once I’ve done the thinking part… and hopefully escape endless Scrum meetings.&lt;/p&gt;

&lt;p&gt;With AI taking fast code generation off the table, it’s time to double down on real skills: problem-solving, clear communication, and many more I cover in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; Because being a good coder is more than mastering syntax.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=ai-is-like-sloppy-junior-coder-bad-memory&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;.&lt;/em&gt; It’s the guide to leveling up my coding skills I wish I had when I was starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>After 600 Posts, These Are My 5 Writing Principles for Consistency</title>
   <link href="https://canro91.github.io/2025/10/19/WritingPrinciples/"/>
   <updated>2025-10-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/19/WritingPrinciples</id>
   <content type="html">&lt;p&gt;I wrote my first blog post in 2018. Well, it was more of a word vomit pretending to be a coding tutorial.&lt;/p&gt;

&lt;p&gt;I’ve been writing &lt;a href=&quot;/2025/09/01/300DailyPosts/&quot;&gt;a daily post since November 1st, 2024&lt;/a&gt;. And in the last year, I’ve written over &lt;a href=&quot;/2025/06/15/LinkedIn/&quot;&gt;300 LinkedIn posts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By pure accident or luck, &lt;a href=&quot;/2025/10/12/1DollarTest/&quot;&gt;I made my first $1 online&lt;/a&gt; thanks to my writing.&lt;/p&gt;

&lt;p&gt;After all these years, I’ve adopted the following 5 guiding principles to keep writing:&lt;/p&gt;

&lt;h2 id=&quot;1-if-it-helps-one-person-hit-post&quot;&gt;#1. If it helps one person, hit post.&lt;/h2&gt;

&lt;p&gt;You don’t need to write to the masses. Just to one person. And that one person could be your past self.&lt;/p&gt;

&lt;h2 id=&quot;2-give-something-and-give-it-fast&quot;&gt;#2. Give something and give it fast.&lt;/h2&gt;

&lt;p&gt;In 17ms, we decide if we keep reading or move on. Credits to &lt;em&gt;Small Brevity&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Online writing (emails, social media posts, or blog posts) isn’t fiction. People expect a long detailed description of scenery and dialog in a novel. Even in fiction, if your story isn’t moving fast enough, readers will put your book aside.&lt;/p&gt;

&lt;p&gt;Get to the point! &lt;a href=&quot;/2025/07/15/Headlines/&quot;&gt;Nail your headlines&lt;/a&gt; and &lt;a href=&quot;/2025/06/04/OpeningLines/&quot;&gt;work on your opening lines&lt;/a&gt;. Cut the long intros.&lt;/p&gt;

&lt;h2 id=&quot;3-write-as-if-nobodys-reading-keep-writing-because-you-never-know-who-is&quot;&gt;#3. Write as if nobody’s reading. Keep writing because you never know who is.&lt;/h2&gt;

&lt;p&gt;Writing feels lonely when you start.&lt;/p&gt;

&lt;p&gt;The cure? &lt;a href=&quot;/2025/10/08/PastSelf/&quot;&gt;Write for your younger self&lt;/a&gt;. Write what you wish you had known 2 years ago.&lt;/p&gt;

&lt;h2 id=&quot;4-dont-wait-to-become-an-expert-write-to-become-one&quot;&gt;#4. Don’t wait to become an expert. Write to become one.&lt;/h2&gt;

&lt;p&gt;If you wait to become an expert, you’ll never write while &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;putting in the 10,000 hours&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead, write to learn. Document your journey. Teach as you go.&lt;/p&gt;

&lt;h2 id=&quot;5-intention-gets-you-started-a-system-keeps-you-showing-up&quot;&gt;#5. Intention gets you started. A system keeps you showing up.&lt;/h2&gt;

&lt;p&gt;Writing can feel easy, especially with AI. Consistency is the real challenge.&lt;/p&gt;

&lt;p&gt;Find ways to capture ideas and turn them into posts.&lt;/p&gt;

&lt;p&gt;In the end, intention starts the journey. But attitude, systems, and habits keep it going.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>8 Things You Should Never Apologize For (or Feel Guilty About)</title>
   <link href="https://canro91.github.io/2025/10/18/FeelingGuilty/"/>
   <updated>2025-10-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/18/FeelingGuilty</id>
   <content type="html">&lt;p&gt;Here are 8 things I believe you should never feel bad about, no matter what:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Saying no.&lt;/strong&gt; To negativity, toxic relationships, bad jobs… To anything your gut instinct doesn’t like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Choosing an unproven path.&lt;/strong&gt; &lt;a href=&quot;/2025/05/07/LifeLesson/&quot;&gt;There’s a default plan&lt;/a&gt; set for you if you don’t choose yours.&lt;/p&gt;

&lt;p&gt;Go to college, get a job, work hard, keep your head down, and retire. That worked for our parents. Not anymore.&lt;/p&gt;

&lt;p&gt;It’s okay to start a small business, create content, or freelance instead of following the 9-5 path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Putting yourself first.&lt;/strong&gt; If you don’t take care of yourself, nobody will. And life is often like airplane safety measures: put your oxygen mask on first, before helping others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Buying books.&lt;/strong&gt; From &lt;em&gt;I Will Teach You to Be Rich&lt;/em&gt; by Ramit Sethi, I learned to cut what we hate and spend on what we love. If that means buying books, so be it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Avoid spending on luxury items.&lt;/strong&gt; Who needs the expensive watch, designer clothes, or fancy wallet? Often, people go into debt to pay for those items. And if it doesn’t put money into your pockets, it’s a liability. (Credits to &lt;a href=&quot;/2025/01/05/MoneyBooks/&quot;&gt;Poor Dad Rich Dad&lt;/a&gt;.) And it’s okay to cut it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Traveling.&lt;/strong&gt; Apart from visiting new places, &lt;a href=&quot;/2020/10/23/ThreeLanguageLessons/&quot;&gt;traveling teaches you new languages&lt;/a&gt; and to expand your horizons. The world isn’t like your small town. People don’t think like you. And that’s okay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Being single.&lt;/strong&gt; I don’t remember where I heard it, but most people start relationships simply because they feel alone. For that, it’s better to find a hobby or new friends than to start a relationship that might hurt someone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Promoting your own work.&lt;/strong&gt; Sometimes we debate sports, politics, or why Apple phones beat Samsung phones, but hesitate to promote our own work. Like it or not, we’re selling all the time. And if we don’t sell something, &lt;a href=&quot;/2025/05/10/Selling/&quot;&gt;we’ll end up selling our time&lt;/a&gt;. And we’re not going to like it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Politics, highlighting, and death of coding</title>
   <link href="https://canro91.github.io/2025/10/17/FridayLinks/"/>
   <updated>2025-10-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/17/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey! Hope your week’s been good.&lt;/p&gt;

&lt;p&gt;A quick update on my book, &lt;em&gt;Street-Smart Coding&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;I’m done addressing feedback from my beta readers. Turns out I had forgotten about a references section. Arrggg!&lt;/p&gt;

&lt;p&gt;I’ve reread my draft countless times, still afraid of typos. My next step? Design the interior of my book. Quick update over.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Now, the 4 links as usual:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; A corporate job is a game of politics. (Just try to make a change in your company.) Here’s a guide on &lt;a href=&quot;https://www.seangoedecke.com/how-to-influence-politics/&quot;&gt;how to play and influence the tech company politics&lt;/a&gt; (8min).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; Like any new coder, junior-me spent hours tweaking my IDE with themes and extensions. Eventually, I settled down to the Solarized theme. And more recently, while looking for a simple theme for live coding and code samples on my books, I found &lt;a href=&quot;https://tonsky.me/blog/syntax-highlighting/&quot;&gt;Alabaster and the rationale behind it&lt;/a&gt; (10min). When everything is highlighted, nothing really stands out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Plastic bags killed basket-making. Is coding next? &lt;a href=&quot;https://medium.com/@jackmckayfletcher/is-software-development-a-dying-craft-419a3e13325e&quot;&gt;Is coding becoming a dying craft?&lt;/a&gt; (3min) No clear answer, just food for thought. For sure, coding is evolving. We’re not punching cards anymore. Maybe, in the future we won’t be typing symbols like we do now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Often great coders get promoted to management, to stop coding eventually. But what if &lt;a href=&quot;https://idiallo.com/blog/stop-trying-to-promote-my-best-engineers&quot;&gt;a promotion meant going deeper instead of moving up and out?&lt;/a&gt; (6min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/10/14/AIRule/&quot;&gt;the one rule I use to code with AI (to avoid losing my skills)&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/10/13/GoodBadExample/&quot;&gt;the best bad coding example I’m using in my book&lt;/a&gt; (2min). Coming up with a good bad example is surprisingly hard, but I think I nailed one.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by…&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=friday-links-politics-highlighting-death-of-coding&quot;&gt;Preorder Street-Smart Coding here&lt;/a&gt; (pay what you want just for these days) and read 5 of the 30 strategies just to start. If you’ve ever typed “how to get better at coding” on Google, YouTube, and more recently on ChatGPT, this is for you.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Two App (or Browser Extension) Ideas to Make Writing and Journaling Easier</title>
   <link href="https://canro91.github.io/2025/10/16/TwoAppIdeas/"/>
   <updated>2025-10-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/16/TwoAppIdeas</id>
   <content type="html">&lt;p&gt;Feel free to build these two ideas. I’m not afraid to share them. &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;I’m an idea machine&lt;/a&gt;. Steal them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. A browser extension to avoid hitting “Post” or “Publish.”&lt;/strong&gt; Not to stop me writing, just to avoid clicking Post instead of Schedule. Handy for Medium or LinkedIn or any other content platform.&lt;/p&gt;

&lt;p&gt;It’s the second or third time I’ve sent an email meant for the next day. I clicked on Post instead of Schedule. Arrggg! Nothing wrong happened. I just don’t want to appear in people’s inboxes like a desperate creator, begging for money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. A simple online app (or page) with a target count of bullet items.&lt;/strong&gt; Recently, I found the 100 thoughts challenge (write 100 thoughts in less than 20 minutes). Credits to &lt;a href=&quot;https://medium.com/@ibanvdz&quot;&gt;Iban Van der Zeyp&lt;/a&gt; on Medium.&lt;/p&gt;

&lt;p&gt;To finish that challenge, I thought of &lt;a href=&quot;https://www.squibler.io/dangerous-writing-prompt-app&quot;&gt;The Most Dangerous Writing App&lt;/a&gt;. You set a word count or time limit and if you stop typing before you hit that target, it erases everything. Gone! Forever. What about something similar with a target bullet point count? And each double Enter adds a new bullet point to the list.&lt;/p&gt;

&lt;p&gt;Maybe those two are good weekend coding challenges to play with AI. If you build one, I’d love to hear about it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Real Reason Why Some Dev Companies Are Forcing AI</title>
   <link href="https://canro91.github.io/2025/10/15/AIRealReason/"/>
   <updated>2025-10-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/15/AIRealReason</id>
   <content type="html">&lt;p&gt;I haven’t stumbled upon any client or company forcing me to use AI.&lt;/p&gt;

&lt;p&gt;That’s not a reality for everybody in the industry. Today I found &lt;a href=&quot;https://www.reddit.com/r/cscareerquestions/comments/1o6vjv0/completely_losing_interest_in_the_career_due_to/&quot;&gt;this Reddit post&lt;/a&gt; from a coder who has lost interest after being forced to use AI, even he was tracked:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Within the span of maybe 2 months my corporate job went from “I’ll be here for life” to “Time to switch careers?” Some exec somewhere in the company decided everyone needs to be talking to AI, and they track how often you’re talking with it. I ended up on a naughty list for the first time in my career, despite never having performance issues. I explain to my manager and his response is to just ask it meaningless questions.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That post rang a bell! It reminded me of a conversation I had recently.&lt;/p&gt;

&lt;h2 id=&quot;the-real-driver-isnt-productivity&quot;&gt;The real driver isn’t productivity&lt;/h2&gt;

&lt;p&gt;These days, I caught-up with some of my ex-coworkers, and one story stood up.&lt;/p&gt;

&lt;p&gt;After the usual chit-chat, one of them shared that his company was encouraging them to use AI, not so strong like the guy from Reddit. Maybe productivity was the official reason.&lt;/p&gt;

&lt;p&gt;But the real reason? Turns out, one of the company founders was also investing in an AI startup. And guess which AI tool they were encouraging people to use.&lt;/p&gt;

&lt;p&gt;Just like I found the other day, if you think of &lt;a href=&quot;/2025/09/10/WhatAIReallyMeans/&quot;&gt;AI as just another subscription company&lt;/a&gt; pushed for profit, all the hype starts to make more sense.&lt;/p&gt;

&lt;p&gt;The real driver isn’t productivity, but financial interest.&lt;/p&gt;

&lt;p&gt;It’s easy to get caught up in the AI hype and forget coding is more than shipping crappy lines of code fast.&lt;/p&gt;

&lt;p&gt;But coding is also about clear communication, thoughtful problem-solving, and knowing when to say no. None of that shows up in AI usage metrics.&lt;/p&gt;

&lt;p&gt;And that’s why wrote, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;, to share the skills I wish I’d learned earlier, the ones that help you become a confident, hype-proof coder.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=real-reason-why-dev-companies-are-forcing-ai&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The One Rule I Follow to Code with AI (Without Losing My Skills)</title>
   <link href="https://canro91.github.io/2025/10/14/AIRule/"/>
   <updated>2025-10-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/14/AIRule</id>
   <content type="html">&lt;p&gt;I haven’t blindly jumped on the AI hype train. But I’m no hater either.&lt;/p&gt;

&lt;p&gt;AI is here to stay. That’s why I’ve tried &lt;a href=&quot;/2025/06/19/CodingWithAI/&quot;&gt;AI to offload tasks while coding&lt;/a&gt;. But after a few weeks, &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;I was so AI dependent&lt;/a&gt; I couldn’t write a simple LINQ query. So I decided to stop relying too much on AI and adopted this one rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use AI outside my IDE or editor.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I’m coding, I like to think I’m a surgeon in an operating room, and AI is my assistant doctor. They hand me tools and monitor my patient, but I’m always in charge.&lt;/p&gt;

&lt;p&gt;To stay in control, I never let AI into my editor. I use it in a browser tab.&lt;/p&gt;

&lt;p&gt;It might seem slower or old-school. But it forces me to decompose my problem and extract relevant code for AI. And once I have an answer, it forces me to make it work on my side. At least, I know if the output actually works.&lt;/p&gt;

&lt;p&gt;Just like code you find on StackOverflow or anywhere else online, don’t use what AI gives you if you don’t understand what it’s doing.&lt;/p&gt;

&lt;p&gt;Don’t let AI touch your code directly either.&lt;/p&gt;

&lt;p&gt;AI is faster at generating code than us. No doubt! But being a good coder isn’t about typing fast. It’s about estimating, communicating with non-tech people, and many more skills I’ve included in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=one-rule-i-follow-code-ai-without-losing-my-skills&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt; and start building future-proof coding skills today.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Best Bad Example I&apos;m Using to Teach Clean Code Principles in My Book</title>
   <link href="https://canro91.github.io/2025/10/13/GoodBadExample/"/>
   <updated>2025-10-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/13/GoodBadExample</id>
   <content type="html">&lt;p&gt;Finding good bad examples is hard.&lt;/p&gt;

&lt;p&gt;I’ve worked with ugly codebases that I don’t want to revisit. But copying and pasting from them isn’t a good idea. Apart from privacy issues, complex business rules and  convoluted code blocks make them unusable for tutorials or lessons.&lt;/p&gt;

&lt;p&gt;A good bad example needs to be messy enough to teach from, but not so broken it confuses readers.&lt;/p&gt;

&lt;h2 id=&quot;movies-and-tv-are-great-teaching-domains&quot;&gt;Movies and TV are great teaching domains&lt;/h2&gt;

&lt;p&gt;Since we have all seen a good movie or gone to the cinema, I’ve shifted to movies and TV shows. They’re familiar enough to use as examples.&lt;/p&gt;

&lt;p&gt;In fact, for my book, &lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/streetsmartcoding&quot;&gt;Street-Smart Coding&lt;/a&gt;&lt;/em&gt;, I chose a ticket pricing example that was clear but messy enough to teach a lesson.&lt;/p&gt;

&lt;p&gt;Here it is:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Simple ticket price logic&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CalculatePrice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieTicketRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticketBasePrice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Saturday&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sunday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ticketBasePrice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reduction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuesday&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Wednesday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;reduction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Age&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Saturday&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DayOfWeek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sunday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ticketBasePrice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalPriceChildren&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticketBasePrice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reduction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Ceiling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ticketBasePrice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finalPrice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticketBasePrice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reduction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Ceiling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finalPrice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A method that prices movie tickets by day and age. Simple enough to highlight common issues, like duplication and branching logic, but not so complex that I need to explain its business rules.&lt;/p&gt;

&lt;p&gt;That code block makes you say “Whaaaat?!!?” in more than one place, but what would you refactor first? Can you spot the bug?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt; isn’t exactly about clean code. It’s a roadmap with 30 strategies to level up your coding skills. Writing code for humans is just one of them.&lt;/p&gt;

&lt;p&gt;Because coding isn’t simply typing symbols fast and mastering syntax. Real coding is also about clear communication, thoughtful problem-solving, and knowing when to say no—and 27 skills more that I cover in the book.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=best-bad-example-teach-clean-code-principles&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>A Simple but Effective Test for Your Online Content Business</title>
   <link href="https://canro91.github.io/2025/10/12/1DollarTest/"/>
   <updated>2025-10-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/12/1DollarTest</id>
   <content type="html">&lt;p&gt;Yesterday, I shared &lt;a href=&quot;/2025/10/11/ContentThatSells/&quot;&gt;the Article Funnel method&lt;/a&gt;, a simple strategy to write content that sells.&lt;/p&gt;

&lt;p&gt;And to test your content-based business (and that method) works, you need to pass &lt;strong&gt;the $1-dollar test.&lt;/strong&gt; That’s another lesson from Mark Thompson, apart from the Article Funnel.&lt;/p&gt;

&lt;p&gt;If what you write generates at least $1 in sales, you have a system that works. You already have posts with a compelling CTA that lead to a landing page for a product readers find valuable. It means you have &lt;a href=&quot;/2024/12/20/CopywritingStudyPlan/&quot;&gt;the right copy&lt;/a&gt; and the right setup.&lt;/p&gt;

&lt;p&gt;With all that in place, from then on, you need to volume and consistency.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Simple Method to Turn Every Post Into a Sales Machine</title>
   <link href="https://canro91.github.io/2025/10/11/ContentThatSells/"/>
   <updated>2025-10-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/11/ContentThatSells</id>
   <content type="html">&lt;p&gt;If you’re afraid of selling, you won’t get a dime.&lt;/p&gt;

&lt;p&gt;But to sell with your content, you don’t have to resort to banners, invasive popups, and countdown timers to trick people into buying.&lt;/p&gt;

&lt;p&gt;From &lt;a href=&quot;https://medium.com/@seriousmarketersonly&quot;&gt;Mark Thompson&lt;/a&gt;, a veteran marketer I followed on Medium, I learned &lt;a href=&quot;https://simpleisprofit.com/article-funnel-challenge&quot;&gt;the Article Funnel method&lt;/a&gt; to turn every post into a sales funnel.&lt;/p&gt;

&lt;p&gt;Here are my takeaways from his method and from following him:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Promote something in every post.&lt;/strong&gt; That’s what Mark does in every post. At the end of his posts, there’s a CTA to a related offer.&lt;/p&gt;

&lt;p&gt;I stopped being afraid of promoting anything in every post when I learned that’s the method &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;famous creators use to make millions online&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Give away something valuable in the post.&lt;/strong&gt; It’s so annoying when you open a post titled “10 ways/tips/lessons to…” and when you open it, you only find one or two items and you have to buy something to access the rest. That’s how you lose readers.&lt;/p&gt;

&lt;p&gt;Don’t be afraid of giving something. That makes you the source of answers. And we always go back to the one who helps us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Use a natural transition to introduce your offer.&lt;/strong&gt; I used to put a divider at the end of my post before adding my CTAs. They felt like an appendix, most people ignored.&lt;/p&gt;

&lt;p&gt;Instead, the Article Funnel suggests to naturally introduce your offer with a transition paragraph, without sounding salesy or desperate. Something as simple as “if this resonates with you, you’re going to like…where I …”&lt;/p&gt;

&lt;p&gt;No tactics or tricks, just helpful content that points to a related offer. That makes your content into a sales representative working 24/7. And that’s the method I’m using from now on to promote my own products.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: FP twisting the Web, LLMs, and nostalgia</title>
   <link href="https://canro91.github.io/2025/10/10/FridayLinks/"/>
   <updated>2025-10-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/10/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey!&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. A couple of years ago, I challenged myself to learn React. And being a backend developer, I missed the simplicity of the old days: Just some logic inside the script tag to bind forms. It turns out adopting &lt;a href=&quot;https://alfy.blog/2025/10/04/how-functional-programming-shaped-modern-frontend.html&quot;&gt;functional principles shaped and twisted the frontend ecosystem&lt;/a&gt; (20min). It has made us reinvent the wheel. Again.&lt;/p&gt;

&lt;p&gt;#2. LLMs replacing human devs? Well, here are &lt;a href=&quot;https://kix.dev/two-things-llm-coding-agents-are-still-bad-at/&quot;&gt;two tasks LLMs are still bad at&lt;/a&gt; (2min). Maybe they’re more like “weird, overconfident interns.”&lt;/p&gt;

&lt;p&gt;#3. It’s never 5 minutes… when you snooze your alarm, when your manager asks you a quick favor, or when you think you can stretch hours of work right before clocking out. Here’s a &lt;a href=&quot;https://alikhil.dev/posts/the-simple-habit-that-saves-my-evenings/&quot;&gt;productivity tip&lt;/a&gt; (4min) to avoid that “5 minutes” turning into working extra hours.&lt;/p&gt;

&lt;p&gt;#4. For nostalgia or history, here’s &lt;a href=&quot;https://cybercultural.com/p/internet-2000/&quot;&gt;how the Internet looked in 2000&lt;/a&gt; (8min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/10/08/PastSelf/&quot;&gt;how to keep creating when it feels pointless&lt;/a&gt; (1min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by…&lt;/p&gt;

&lt;p&gt;I’m currently putting the final polish on my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.”&lt;/em&gt; While I finish up, you can &lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;preorder your digital copy&lt;/a&gt; and read a preview featuring 5 of the 30 tips, just to get you started.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Steal This Strategy: How to Work with Beta Readers to Launch Your Book</title>
   <link href="https://canro91.github.io/2025/10/09/BetaReaders/"/>
   <updated>2025-10-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/09/BetaReaders</id>
   <content type="html">&lt;p&gt;I stole the process of working with beta readers. Borrow it, let’s say.&lt;/p&gt;

&lt;p&gt;I’ve been a technical editor for Manning, a publishing company that specializes in coding books. And after going through the process twice, I copy their process to work with beta readers for &lt;a href=&quot;/2025/09/21/30Ways/&quot;&gt;my next coding book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s what I did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Have something decent to share.&lt;/strong&gt; Beta readers are the first eyeballs that read your draft. You don’t need a polished or finished book, just a decent draft someone can read. It’s OK if there are typos here and there. The goal isn’t to line edit it, but to refine the overall book structure and content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Create a list of 10 people that can help.&lt;/strong&gt; &lt;a href=&quot;/2025/06/17/TenIdeas/&quot;&gt;I’m a fan of 10-idea lists&lt;/a&gt;, so that was an excuse to write another one.&lt;/p&gt;

&lt;p&gt;I asked LinkedIn connections and friends for help. I used Google Docs to share the draft. Manning has a custom platform for that. I did it the DIY way.&lt;/p&gt;

&lt;p&gt;I shared half of my draft (15 of the 30 chapters I had planned) with half of my beta readers, and the other half to the rest. I didn’t want to overwhelm them with too many pages. They’re busy and helping for free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Offer something in return.&lt;/strong&gt; This is a strategy I copied from Manning. I offered a free digital copy, a mention in the Acknowledgments section, and a shout-out in a LinkedIn post on launching day. Well, Manning doesn’t offer a shout out. I did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Ask for specific feedback.&lt;/strong&gt; I asked them not to focus too much on typos or grammar issues.&lt;/p&gt;

&lt;p&gt;Here’s a list of questions I asked my beta readers to answer after their review.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What did you like the most? What did you like the least?&lt;/li&gt;
  &lt;li&gt;Any repetitive parts? Boring parts? Confusing parts?&lt;/li&gt;
  &lt;li&gt;Any factual errors?&lt;/li&gt;
  &lt;li&gt;Does the content deliver on the book promise?&lt;/li&gt;
  &lt;li&gt;What can I do better? What should I include or remove?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I put those questions on page one of the Google Doc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Share a deadline.&lt;/strong&gt; I forgot to tell my beta readers my launch date. Some of them gave feedback after a couple of days. Others never shared anything. I guess they were busy and I didn’t set a deadline. My bad!&lt;/p&gt;

&lt;p&gt;And here’s the best piece of advice I’ve found: Listen to what beta readers say is wrong, but don’t rely on them for how to fix it. That’s your job as the writer.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Keep Creating When It Feels Pointless</title>
   <link href="https://canro91.github.io/2025/10/08/PastSelf/"/>
   <updated>2025-10-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/08/PastSelf</id>
   <content type="html">&lt;p&gt;The first days of writing or creating anything online are the hardest.&lt;/p&gt;

&lt;p&gt;Nothing seems to work. Nobody seems to care. You feel like what you’re doing is pointless.&lt;/p&gt;

&lt;p&gt;I know that feeling. Just a couple of my coworkers read &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;my first blog posts&lt;/a&gt;. And when &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;I started on LinkedIn&lt;/a&gt;, two likes and one comment felt like going viral.&lt;/p&gt;

&lt;p&gt;But to endure that phase of obscurity, &lt;a href=&quot;https://www.jeetmehta.com/posts/thrive-in-obscurity&quot;&gt;Jeet Mehta shared three points&lt;/a&gt;. The second one really resonated,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Your audience is just you, pushed outwards.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You are your own audience. Create for your past self. &lt;a href=&quot;/2025/10/07/MiniBooks/&quot;&gt;Write a book for who you used to be&lt;/a&gt;. Write the tutorial or guide you wish you had found. Create to help one person. And that one could be your past self.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Don&apos;t Write the Next Atomic Habits. Write Mini-Books</title>
   <link href="https://canro91.github.io/2025/10/07/MiniBooks/"/>
   <updated>2025-10-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/07/MiniBooks</id>
   <content type="html">&lt;p&gt;You don’t need a publisher and 100,000 words to write a book that matters.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://substack.com/@minibookmodel&quot;&gt;Chris Stanley&lt;/a&gt; was the one who finally debunked my ideas about &lt;a href=&quot;/2025/07/22/Books/&quot;&gt;writing books&lt;/a&gt;. He’s the author of 20 Amazon best-sellers. He lives off his books, traveling around the U.S. in a boat. He’s the “Mini Book” guy and now he teaches how to write them.&lt;/p&gt;

&lt;p&gt;Today I attended a workshop with Chris, where he showed a glimpse of his method. Here are 10 lessons I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Don’t think of funnels, think of a flywheel.&lt;/strong&gt; Think of an ecosystem of content, with mini-books at its core, that changes people’s beliefs and turns them into fans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Forget about writing the next New York Times best seller.&lt;/strong&gt; When Chris started publishing his books, he noticed his shorter books did better. It’s not a coincidence. 75% of people don’t finish the books they buy.&lt;/p&gt;

&lt;p&gt;The solution? Write mini-books. Just the good parts of a traditional book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. A mini-book offers one solution to one problem for one person.&lt;/strong&gt; Your solution should be something you’ve tried and proven yourself. The more specific your person, the better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. A mini-book should be SMART:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Structured: It should be well structured and easy to follow.&lt;/li&gt;
  &lt;li&gt;Mini: A word count isn’t the goal, but a mini-book usually has between 10-15K words. Write a book readers can finish in one or two hours.&lt;/li&gt;
  &lt;li&gt;Actionable: It should offer a solution your readers can apply immediately.&lt;/li&gt;
  &lt;li&gt;Repeatable: It should present a message easy to remember and share.&lt;/li&gt;
  &lt;li&gt;Transformational: It shouldn’t only teach something, but should change beliefs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, Chris’ &lt;em&gt;Mini Book Model&lt;/em&gt; has only 129 pages and he breaks down his model so you can write a book in days, not months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. There are only 3 people to write a book for:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Who you were: That’s your past self. Maybe that’s a first-time business owner or a junior coder.&lt;/li&gt;
  &lt;li&gt;Who you are: That’s your current self, your identity, and what you do now.&lt;/li&gt;
  &lt;li&gt;Who you love: That’s someone you care about and want to help. Chris once wrote a book for his wife. And we could write &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;a book for our future generations&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;#6. Write a clever title and a clear subtitle.&lt;/strong&gt; Once you have a problem and solution, your title should create a hook and your subtitle should create a promise. For example, &lt;em&gt;Mini Book Model: How to Write Your Big Ideas in Small Books&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. There are three ways to outline a book:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;W’s: That’s the what, when, where, why, and how. Write a chapter to answer each question. For a book about mini-books, the first chapter could be what a mini-book is. The next chapter, why write a mini-book.&lt;/li&gt;
  &lt;li&gt;Step by step: Write one step of your solution per chapter.&lt;/li&gt;
  &lt;li&gt;Problem/solution: Each chapter presents a problem and offers a solution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Again, in &lt;em&gt;Mini Book Model&lt;/em&gt;, Chris used the W’s technique to present his mini-book concept.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Make your solution memorable.&lt;/strong&gt; If you’re using the step-by-step outline technique, use an acronym or a visual metaphor to guide your readers and help them remember your solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. As a bonus, use chapters of your next book as preview.&lt;/strong&gt; It makes your book thick enough to display your name and title on the spine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Not only write books, but create a whole identity and brand around them.&lt;/strong&gt; After 20 books or more, Chris is the “Mini Book” guy. His emails, tagline, and even book titles use the same concept. Mini-books everywhere. That’s how he stands out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Mini books have become my go-to way of sharing ideas in coding and personal growth. If you’re curious, you’ll find &lt;a href=&quot;/books&quot;&gt;my books here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Quick Steps to Edit Your Posts Fast (In Minutes, not Hours)</title>
   <link href="https://canro91.github.io/2025/10/06/Editing/"/>
   <updated>2025-10-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/06/Editing</id>
   <content type="html">&lt;ol&gt;
  &lt;li&gt;Do a first pass, looking for typos&lt;/li&gt;
  &lt;li&gt;Use Grammarly or &lt;a href=&quot;/2025/03/01/ReplacingGrammarly/&quot;&gt;an AI prompt to check grammar&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/07/15/Headlines/&quot;&gt;Tweak the headline&lt;/a&gt; and &lt;a href=&quot;/2025/06/04/OpeningLines/&quot;&gt;opening&lt;/a&gt; and closing lines&lt;/li&gt;
  &lt;li&gt;Look for sentences to simplify&lt;/li&gt;
  &lt;li&gt;Use a text-to-speech tool and listen to it at 2x&lt;/li&gt;
  &lt;li&gt;Look for opportunities to link to related posts or resources&lt;/li&gt;
  &lt;li&gt;Enjoy, you’ve just saved hours and polished your post!&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>What Happened Around This Day In My Blog</title>
   <link href="https://canro91.github.io/2025/10/05/AroundThisDate/"/>
   <updated>2025-10-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/05/AroundThisDate</id>
   <content type="html">&lt;p&gt;My blog is my time capsule. Today, I opened it to find out what I was into. Here’s what I found.&lt;/p&gt;

&lt;p&gt;In 2020, exactly on this date, I wrote about &lt;a href=&quot;/2020/10/05/CompareDateTimeSQLServer/&quot;&gt;comparing dates without the time part in SQL Server&lt;/a&gt;. At that time, I was learning SQL Server performance tuning with &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar’s courses&lt;/a&gt; and his writing style inspired most of my SQL posts.&lt;/p&gt;

&lt;p&gt;In 2022, I was contracting with an American client and we were trying to implement DDD in their new projects. I shared my notes on the book &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;Hands-on Domain-Driven Design with .NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And in 2023, I was already &lt;a href=&quot;/2023/10/02/MondayLinks/&quot;&gt;compiling four interested links about coding&lt;/a&gt; every Monday. That was the seed to later start &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;Friday Links&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At that time, I was playing the SEO game. I can notice it: I wrote question/answer posts with a bolded paragraph answering the post question. I was hoping to win the target answers on Google search results. Since then, I moved away from the SEO game and &lt;a href=&quot;/2025/01/27/TimesOfDeadBlogging/&quot;&gt;changed my blogging strategy&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Could&apos;ve Finished My Book in Half the Time—If I&apos;d Known These Two Lessons</title>
   <link href="https://canro91.github.io/2025/10/04/ABookFaster/"/>
   <updated>2025-10-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/04/ABookFaster</id>
   <content type="html">&lt;p&gt;It took me 131 days to finish the first draft of &lt;a href=&quot;/2025/09/21/30Ways/&quot;&gt;my next book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s 4 months and 8 days from creating a new file to typing the last word. I could have finished two books in that time. &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt; challenges his podcast listeners to &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;finish a non-fiction book in 30 days&lt;/a&gt;. By that standard, I could have even finished four.&lt;/p&gt;

&lt;p&gt;Looking back, two lessons could’ve cut that time in half.&lt;/p&gt;

&lt;h2 id=&quot;1-make-it-a-priority&quot;&gt;#1. Make it a priority&lt;/h2&gt;

&lt;p&gt;At some point, finishing the first draft felt like a mission impossible.&lt;/p&gt;

&lt;p&gt;I was balancing multiple projects at the same time: I was blogging, &lt;a href=&quot;/2025/08/05/LinkedInStrategy/&quot;&gt;growing my LinkedIn account&lt;/a&gt;, staying active in a ghostwriting community, and coding on the side.&lt;/p&gt;

&lt;p&gt;After realizing &lt;a href=&quot;/2025/07/08/TheOnlyProductivityTip/&quot;&gt;I was exhausted&lt;/a&gt; from juggling all those projects, I focused only on my blog and the draft to keep momentum, even when all other projects suffered.&lt;/p&gt;

&lt;h2 id=&quot;2-take-one-step-a-day&quot;&gt;#2. Take one step a day&lt;/h2&gt;

&lt;p&gt;Of course, writing a book is a daunting task. It’s worse when you try to finish multiple projects.&lt;/p&gt;

&lt;p&gt;Some days, life gets in the way and you can’t write. But take a small step toward finishing your book every day. Maybe it’s finding beta readers, scrolling Amazon for cover inspiration, making promotional material, or running a poll on social media to choose a title.&lt;/p&gt;

&lt;p&gt;One small action every day. That’s all you need. Even if that action is not writing. Your book will thank you. And I would have finished my book in half the time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Junior lessons, side projects, and AI</title>
   <link href="https://canro91.github.io/2025/10/03/FridayLinks/"/>
   <updated>2025-10-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/03/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Today I have 3 short links for you:&lt;/p&gt;

&lt;p&gt;#1. Here are &lt;a href=&quot;https://raheeljunaid.com/blog/advice-for-developers/&quot;&gt;10+ points a coder wishes he knew as a junior&lt;/a&gt; (4min). I can’t disagree with his take that coding is the easy part of the job.&lt;/p&gt;

&lt;p&gt;#2. Not sure about finishing a side project or starting a new one? &lt;a href=&quot;https://cassidoo.co/post/questions-when-i-need-to-finish-something/&quot;&gt;Ask yourself these questions&lt;/a&gt; (2min).&lt;/p&gt;

&lt;p&gt;#3. I’m still trying to figure out how to use AI for coding. Here’s &lt;a href=&quot;https://antonz.org/write-code/&quot;&gt;“an imprecise, slow and terribly painful way to get things done”&lt;/a&gt; with AI and what to do instead. (2min)&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/09/29/CodeReviewCulture/&quot;&gt;4 ideas to buildl a code review culture your team will love&lt;/a&gt; (4min) and &lt;a href=&quot;/2025/10/01/MakeLovedOnesLiveForever/&quot;&gt;how to preserve our loved one stories&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>7 Fresh Alternatives to LinkedIn&apos;s Most Overused Cliché</title>
   <link href="https://canro91.github.io/2025/10/02/ConsistencyIsKey/"/>
   <updated>2025-10-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/02/ConsistencyIsKey</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;/2025/06/15/LinkedIn/&quot;&gt;Join the LinkedIn creator crowd&lt;/a&gt; and you’ll hear one tired phrase everywhere.&lt;/p&gt;

&lt;p&gt;“Consistency is key.”&lt;/p&gt;

&lt;p&gt;One influencer said it, and everybody trying to sound smart repeated it. And now it’s everywhere.&lt;/p&gt;

&lt;p&gt;I roll my eyes every time I see it, &lt;a href=&quot;/2024/12/14/Consistency/&quot;&gt;not because it’s wrong&lt;/a&gt;, but because it’s overused. It’s the “synergies,” “alignment,” and “disruption.” It’s the corporate lingo nobody uses it in a real conversation.&lt;/p&gt;

&lt;p&gt;I’m so sick of it, I had to rephrase it.&lt;/p&gt;

&lt;p&gt;Here are my 7 alternatives to “Consistency is key:”&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Don’t break the posting chain&lt;/li&gt;
  &lt;li&gt;Show up as if your life depends on it&lt;/li&gt;
  &lt;li&gt;No successful brand was built in a day&lt;/li&gt;
  &lt;li&gt;Post until people recognize you from the feed&lt;/li&gt;
  &lt;li&gt;Show up until it becomes second nature&lt;/li&gt;
  &lt;li&gt;You only lose if you stop playing&lt;/li&gt;
  &lt;li&gt;Just keep doing it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I promise I won’t say “Consistency is key” again. Because every time we say it, a cute baby panda dies in the forest. Let’s save pandas by retiring this and other overused clichés for good.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Simplest Way to Make Our Loved Ones Stories Live Forever</title>
   <link href="https://canro91.github.io/2025/10/01/MakeLovedOnesLiveForever/"/>
   <updated>2025-10-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/10/01/MakeLovedOnesLiveForever</id>
   <content type="html">&lt;p&gt;Imagine an FBI agent arresting a Nazi spy while someone else urges his country to declare war against Germany.&lt;/p&gt;

&lt;p&gt;That’s not a Netflix show. It’s a true story &lt;a href=&quot;https://www.linkedin.com/in/flabastida/&quot;&gt;Fernando Labastida&lt;/a&gt; shared on LinkedIn. The two men were his grandparents. Here’s &lt;a href=&quot;https://www.linkedin.com/posts/flabastida_one-of-them-was-an-fbi-agent-who-arrested-activity-7378742940275318784-urcW&quot;&gt;the original post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I read Fernando’s post, my first thought was “this should be a book.” Who wouldn’t like to read a story about arresting Nazis and the political discussions about Mexico entering WWWII? Yes, this story happened in Mexico.&lt;/p&gt;

&lt;p&gt;I suggested he write a book, along with a list of mini-stories to include in that book:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Each of his granddads’ backstories.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The main event of their lives&lt;/strong&gt;: arresting the Nazi spy and starting the movement to go to war.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The crossing point of the two stories.&lt;/strong&gt; Their daughter and son, respectively, got married. And 40 years later, a family wedding took place in the same building where the Nazi spy was captured.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;A summary of what was happening at that time in Mexico&lt;/strong&gt; and the other places where the story took place.  It turns out that a German U-boat sank a Mexican oil tanker.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;A story of how Fernando found out about his granddads.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I found that story so intriguing that I imagined the first scene of a book or TV show:&lt;/p&gt;

&lt;p&gt;One of his granddads packs to go to Mexico after briefing the FBI top officials, while the other rehearses a speech he’s about to give in Mexico City’s main square. Of course, that’s with time jumps and scene breaks.&lt;/p&gt;

&lt;p&gt;Fernando said his granddads have passed away, and only a few relatives know the full story. That story could get lost in newspapers and government archives only journalist read, if he doesn’t write it.&lt;/p&gt;

&lt;p&gt;A book is the best way to preserve family stories and honor our loved ones. There’s no need to &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;write a New York Times bestseller to tell family stories&lt;/a&gt;. Even if only our future kids and grandkids read it, that’s enough to keep the story alive.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Surprisingly Effective Way to Promote Your First Book</title>
   <link href="https://canro91.github.io/2025/09/30/PromotingABook/"/>
   <updated>2025-09-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/30/PromotingABook</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;/2025/09/21/30Ways/&quot;&gt;With a book coming out&lt;/a&gt;, my next question is how to promote it.&lt;/p&gt;

&lt;h2 id=&quot;the-obvious-strategy&quot;&gt;The obvious strategy&lt;/h2&gt;

&lt;p&gt;The first (and maybe obvious) strategy I’ve found is to share everything about it.&lt;/p&gt;

&lt;p&gt;Share when the first draft is done, the edits you left out, the book cover ideas, some excerpts, and the reviews you get. Even I found the idea of a guy who wears a “Ask me about my book” t-shirt when he goes to conferences and networking events to promote his books.&lt;/p&gt;

&lt;p&gt;Share anything and everything about your book.&lt;/p&gt;

&lt;h2 id=&quot;the-surprising-strategy&quot;&gt;The surprising strategy&lt;/h2&gt;

&lt;p&gt;But apart from making noise, I found a surprising strategy:&lt;/p&gt;

&lt;p&gt;Write a second or a third book.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, one of my favorite writers, teaches that strategy in his podcast, posts, and course. “The best way to promote a book is to write another book.” Because when you like a book, you want to binge-read all other books from the same author.&lt;/p&gt;

&lt;p&gt;That happened to me. By accident, I found &lt;a href=&quot;/2025/01/19/ChooseYourselfGuideToWealth/&quot;&gt;one of James Altucher’s books&lt;/a&gt; and &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;I almost devoured all of them&lt;/a&gt;. So the strategy works. It worked with me. And that’s proof enough.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>4 Ideas To Build a Code Review Culture Your Team Will Love</title>
   <link href="https://canro91.github.io/2025/09/29/CodeReviewCulture/"/>
   <updated>2025-09-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/29/CodeReviewCulture</id>
   <content type="html">&lt;p&gt;After sharing two strategies for faster code reviews, I got a comment on Medium that captured the struggle of building a code review culture.&lt;/p&gt;

&lt;p&gt;Here it is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I am trying to get my team to craft smaller PRs as a means to faster reviews, but I am always bombarded with questions like these:&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;How can someone review the first without knowing what it will be used for? How can the reviewer have the context for the change? And what if the reviewer suggests changes to the 3rd PR that will incur changes to the second or the first?&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;I get the questions, and I get the pushback, but how can we find a happy middle ground?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve had the same questions as the ones in the comment. I realized how painful code reviews are only after reviewing PRs. That was what made me adopt &lt;a href=&quot;/2025/09/04/FasterCodeReviews/&quot;&gt;those two strategies&lt;/a&gt; I had already shared.&lt;/p&gt;

&lt;p&gt;Here are my two cents to make code reviews smoother and find that “happy middle ground:”&lt;/p&gt;

&lt;h2 id=&quot;1-have-clear-guidelines-on-what-to-review&quot;&gt;#1. Have clear guidelines on what to review&lt;/h2&gt;

&lt;p&gt;Code reviews are pointless if we only discuss variable names or nitpick on code style.&lt;/p&gt;

&lt;p&gt;The easy fix to avoid discussing formatting:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/03/10/AutomateCodeStyle/&quot;&gt;Automate code style and conventions&lt;/a&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet format&lt;/code&gt;, prettier, or similar tools.&lt;/li&gt;
  &lt;li&gt;Use a sample class or module to show naming styles and conventions, instead of documentation nobody ever updates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of focusing on nitpicks, define the goal of your code reviews. Is it to mentor newcomers, enforce good practices, find bugs? Or all of the above?&lt;/p&gt;

&lt;h2 id=&quot;2-always-give-and-ask-for-context&quot;&gt;#2. Always give and ask for context&lt;/h2&gt;

&lt;p&gt;At a previous team, we had the guideline to always include a JIRA ticket number in the PR title. You can adopt that one.&lt;/p&gt;

&lt;p&gt;If you’re a reviewee, always include a good title and a short description, even if you’re linking to your JIRA ticket. Often, JIRA tickets only describe business requirements, not technical details.&lt;/p&gt;

&lt;p&gt;Now if you’re a reviewer, feel free to pass a PR to the right team member if you don’t have enough context. Or you could take it as an advantage and approach the review process from a beginner’s perspective.&lt;/p&gt;

&lt;h2 id=&quot;3-reduce-ceremonies-or-make-prs-easy-to-open-and-merge&quot;&gt;#3. Reduce ceremonies (or make PRs easy to open and merge)&lt;/h2&gt;

&lt;p&gt;At another past job, we had to fill out a form with every review.&lt;/p&gt;

&lt;p&gt;It was a stupid rule to comply with a company certification or something. And on top of that, we didn’t have a web-based collaborative tool. The reviewer had to sit with the reviewee, go through the code, and fill out a form with the “findings.” And that was for every round of review. Arrggg!&lt;/p&gt;

&lt;p&gt;Make PRs easy to open, review, and merge.&lt;/p&gt;

&lt;h2 id=&quot;4-involve-all-team-members&quot;&gt;#4. Involve all team members&lt;/h2&gt;

&lt;p&gt;To create awareness and spread change, involve all team members, even informally.&lt;/p&gt;

&lt;p&gt;To involve your team, steal this guideline from a past job. We only merged PRs after approvals:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;From a “default” reviewer. This was someone in charge only of code reviews across the whole company. I know most companies can’t afford this role.&lt;/li&gt;
  &lt;li&gt;From the repository owner, if the PR went to a codebase from another team.&lt;/li&gt;
  &lt;li&gt;From the team leader&lt;/li&gt;
  &lt;li&gt;From another team member&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was only after my stint as a code reviewer that I learned to &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;give valuable feedback&lt;/a&gt; and clean my PRs.&lt;/p&gt;

&lt;p&gt;Code reviews should be everyone’s responsibility.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting thought&lt;/h2&gt;

&lt;p&gt;Let me tell you, making this kind of change is hard.&lt;/p&gt;

&lt;p&gt;As a default code reviewer, it took me months to see people picking up the practices I was preaching, like short PRs and using Conventional Comments.&lt;/p&gt;

&lt;p&gt;To spread the change, enforce it (like reject large PRs) or lead by example. The first one is faster. Or try combining the two: a hard rule after an educational campaign. It takes time, but it starts with one PR.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Writing Lesson a Friend Taught Me (and He&apos;s Not a Writer)</title>
   <link href="https://canro91.github.io/2025/09/28/TooLong/"/>
   <updated>2025-09-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/28/TooLong</id>
   <content type="html">&lt;p&gt;After maybe a month, I caught up with an old friend and got a surprise critique.&lt;/p&gt;

&lt;p&gt;The first thing he told me was, “I had some time without checking LinkedIn. I found one of your posts, but it was too long and I stopped reading midway.” We both laughed. Ouch!&lt;/p&gt;

&lt;p&gt;When my friend said that, I realized I do the same thing. I skip big blocks of text on LinkedIn and blog posts.&lt;/p&gt;

&lt;p&gt;Truth is, people scroll social media for dopamine, not for depth. And we should write for them, too. We should use attention-grabbing opening lines and short, well-formatted posts.&lt;/p&gt;

&lt;p&gt;Graffiti, social media posts, essays, book chapters…They’re all different. Readers come with different goals. The platform sets its own rules and expectations. Our job is to write in a way that fits.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Six Clever Writing Tricks in Netflix&apos;s Black Doves</title>
   <link href="https://canro91.github.io/2025/09/27/BlackDoves/"/>
   <updated>2025-09-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/27/BlackDoves</id>
   <content type="html">&lt;p&gt;I binge-watched another TV show again. And to not feel guilty, I’m writing about it.&lt;/p&gt;

&lt;p&gt;This time, I watched &lt;a href=&quot;https://www.netflix.com/co-en/title/81682935&quot;&gt;Black Doves&lt;/a&gt; on Netflix, an espionage and action story that takes place in present-day London. The Black Doves, a private intelligence firm, placed an agent close to a top government official to steal secrets and sell them.&lt;/p&gt;

&lt;h2 id=&quot;after-binge-watching-the-6-episodes-here-are-the-writing-devices-i-noticed&quot;&gt;After binge-watching the 6 episodes, here are the writing devices I noticed:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. It starts with action, blood, and mystery.&lt;/strong&gt; The first scene shows a triple killing. Enough action to hook anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Every episode complicates the plot more.&lt;/strong&gt; It felt like watching &lt;em&gt;The Mandalorian&lt;/em&gt;, where each alliance pulls Mando away from his goal. The same thing happens in this show.&lt;/p&gt;

&lt;p&gt;A simple revenge turns into an entangled plot that involves Britain’s political stability and possibly sparks the next World War.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Almost every episode starts with a time jump&lt;/strong&gt; to show us the background story of our characters. We see how Helen is recruited and how she turns from an amateur agent into a cold-blooded killer with a double life.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. It uses flashbacks to tell us when Helen is lying.&lt;/strong&gt; The Black Doves have one rule they take seriously: nobody should know about the existence of the organization. But Helen broke this rule and she lies about it all the time. And through flashbacks, we see the truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Instead of an omniscient narrator, we get to know about Helen’s past through an interview.&lt;/strong&gt; She applies to an international company, where her past is questioned during the interview. We get to know her without boring dialog or narration. Clever trick!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. They use a line as a connecting element.&lt;/strong&gt; “If one door closes, a window opens elsewhere,” or something like that. We hear it in almost every episode. It keeps the plot going in spite of every setback.&lt;/p&gt;

&lt;p&gt;Other TV show breakdowns? &lt;a href=&quot;/2025/07/28/Chespirito/&quot;&gt;Not Really on Purpose&lt;/a&gt;, &lt;a href=&quot;/2025/05/18/HouseMD/&quot;&gt;House M.D.&lt;/a&gt;, and &lt;a href=&quot;/2025/03/13/SixTripleEight/&quot;&gt;Six Triple Eight&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Leading, vibe coding cleanup, and performance</title>
   <link href="https://canro91.github.io/2025/09/26/FridayLinks/"/>
   <updated>2025-09-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/26/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. Being a good tech leader isn’t about knowing all the answers. Here’s &lt;a href=&quot;https://idiallo.com/blog/how-to-lead-in-a-room-full-of-experts&quot;&gt;how a good leader leads a room full of expert&lt;/a&gt; (7min). I loved this piece so much that I wrote a reaction post.&lt;/p&gt;

&lt;p&gt;#2. Ten years ago, the junior me wanted to optimize every line of code. Newbie mistake. I wish I had found this guide with &lt;a href=&quot;https://johnnysswlab.com/9-things-every-fresh-graduate-should-know-about-software-performance/&quot;&gt;9 things every graduate should know about performance&lt;/a&gt; (5min).&lt;/p&gt;

&lt;p&gt;#3. Hey this is kind-of a pitch, but the new coding gig is &lt;a href=&quot;https://donado.co/en/articles/2025-09-16-vibe-coding-cleanup-as-a-service/&quot;&gt;vibe-coding clean up&lt;/a&gt; (6min). It doesn’t sound that bad.&lt;/p&gt;

&lt;p&gt;#4. Without getting into politics, here’s a breakdown of &lt;a href=&quot;https://gist.github.com/avestura/ce2aa6e55dad783b1aba946161d5fef4&quot;&gt;how it’s like using online services from a “banned” country&lt;/a&gt; (6min). Well, from that posts I learned about a HTTP response code.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/09/23/Reinvention/&quot;&gt;the first step for reinvention when we feel lost&lt;/a&gt; (2min) and I reacted to &lt;a href=&quot;/2025/09/24/Leadership/&quot;&gt;how to lead in a room full of experts&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>A Masterclass in Opening Lines from a Nobel-winning Writer</title>
   <link href="https://canro91.github.io/2025/09/25/OpeningLine/"/>
   <updated>2025-09-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/25/OpeningLine</id>
   <content type="html">&lt;p&gt;I have a new writing obsession: Noticing and dissecting the opening lines of books.&lt;/p&gt;

&lt;p&gt;I should blame &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, one of my favorite writers, for this obsession. His advice taught me to &lt;a href=&quot;/2025/05/09/BleedInTheFirstLine/&quot;&gt;master opening lines&lt;/a&gt; by dissecting the best fiction writers.&lt;/p&gt;

&lt;p&gt;And this time, to apply that lesson, I studied &lt;em&gt;Of Love and Other Demons&lt;/em&gt; by Gabriel Garcia Marquez, a Colombian writer who won a Nobel prize. He’s a master of opening lines. And this one is no exception.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;An ash-gray dog with a white blaze on its forehead burst onto the rough terrain of the market on the first Sunday in December, knocked down tables of fried food, overturned Indians’ stalls and lottery kiosks, and bit four people who happened to cross its path. Three of them were black slaves. The fourth, Sierva Maria de Todos los Angeles, the only child of the Marquis de Casalduero, had come there with a mulatta servant to buy a string of bells for the celebration of her twelfth birthday.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, that wasn’t just an opening line, but a paragraph. Let’s break it down:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. This intro starts with huge drama.&lt;/strong&gt; A dog is creating an unbelievable mess in the market. It has a white blaze on its forehead. This detail becomes relevant later. But no spoilers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. It uses the rule of three&lt;/strong&gt; to present the disaster the dog created:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;“knocked down tables…”&lt;/li&gt;
  &lt;li&gt;“overturned Indian’s stalls…”&lt;/li&gt;
  &lt;li&gt;“bit four people…”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;#3. It presents the main character in the middle of the action.&lt;/strong&gt; That is Sierva Maria. The dog also bit her. This is the starting incident of the entire story.&lt;/p&gt;

&lt;p&gt;We also learn more about Sierva. She’s the daughter of a Marquis and she’s about to turn 12. And she’s placed among slaves and servants. Another detail that becomes relevant later.&lt;/p&gt;

&lt;p&gt;The formula? Drama followed by a character trapped in the middle of it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>re: How to Lead in a Room Full of Experts</title>
   <link href="https://canro91.github.io/2025/09/24/Leadership/"/>
   <updated>2025-09-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/24/Leadership</id>
   <content type="html">&lt;p&gt;Being the smartest in the room doesn’t mean you’re ready to lead it.&lt;/p&gt;

&lt;p&gt;Doing a job well isn’t the same as leading others who do it. Being a soccer player isn’t the same as being the team coach. They need different skill sets.&lt;/p&gt;

&lt;p&gt;Ibrahim Diallo makes a good point about &lt;a href=&quot;https://idiallo.com/blog/how-to-lead-in-a-room-full-of-experts&quot;&gt;leading a room full of experts&lt;/a&gt;. Here are three of his points and my reactions:&lt;/p&gt;

&lt;h2 id=&quot;technical-credibility-gives-you-a-seat-but-your-social-skills-make-you-shine&quot;&gt;Technical credibility gives you a seat. But your social skills make you shine&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;In a room full of experts, your technical credibility gets you a seat at the table, but your social skills determine whether anything productive happens once you’re there.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I learned this lesson in meetings with the CEO and other executives at a past job.&lt;/p&gt;

&lt;p&gt;It was at a small tech shop in my city. I was a Software Engineer I, first of five “ranks” in the company’s career ladder. My hard work gave me a seat on the table. &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;I don’t recommend hard work anymore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The CEO was a coder turned businessman. Sure, he understood a lot of jargon and technical details. But most of the time, he replied with “I don’t care” or “You’re insane” when we dropped technical terms.&lt;/p&gt;

&lt;p&gt;By trial and error, I learned to &lt;a href=&quot;/2024/11/15/TalkingToNonTechies/&quot;&gt;translate technical language into business language&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;your-goal-is-to-find-the-right-person-for-each-task&quot;&gt;Your goal is to find the right person for each task&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Your value [as a leader] isn’t in having all the expertise. It’s in recognizing which expertise is needed when, and creating space for the right people to contribute their best work.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/08/BeingATeamLeader/&quot;&gt;I’ve seen it all around me&lt;/a&gt;. A newly promoted coder who still acts like a rock star, doing all the work. Focusing on lines of code and pull requests instead of projects, milestones, and team dynamics. That’s a recipe for burnout for the leader and failure for the team.&lt;/p&gt;

&lt;p&gt;The perfect example of good leadership and delegation comes from the movie Ocean’s 11 and its sequels.&lt;/p&gt;

&lt;p&gt;In every movie, there’s an impossible item to steal. Of course, that’s never a solo job. It takes a team. The leader presents the challenge and a solution plan.&lt;/p&gt;

&lt;p&gt;But each team member brings their expertise: the locksmith to open a vault, the social guy to extract valuable intel from a person of interest, the gymnastics guy to break into impenetrable rooms, and the explosives guy to create a distraction.&lt;/p&gt;

&lt;p&gt;Follow the same idea from Ocean’s 11, but replace the robbery with a project and all specialties with the right ones.&lt;/p&gt;

&lt;h2 id=&quot;be-comfortable-not-having-all-the-answers&quot;&gt;Be comfortable not having all the answers&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The more comfortable you become with not being the expert, the more effective you become as a leader.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A business book taught me: a good entrepreneur should manage and lead a team of people way smarter than themself. The same is true for teams in charge of knowledge work.&lt;/p&gt;

&lt;p&gt;As a tech leader, learn to lead people smarter than you. Otherwise, you will always lead a team of junior people. A good leader finds the right person for the right job and makes them thrive.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The First Step to Reinvent Your Life (When You Feel Lost)</title>
   <link href="https://canro91.github.io/2025/09/23/Reinvention/"/>
   <updated>2025-09-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/23/Reinvention</id>
   <content type="html">&lt;p&gt;Today, I found a post promising an “upgraded” roadmap for growth in life.&lt;/p&gt;

&lt;p&gt;I found it in a LinkedIn post. I know. &lt;a href=&quot;/2025/07/27/LinkedInWeirdness/&quot;&gt;LinkedIn is a weird place&lt;/a&gt;. Full of shallow advice and “see my perfect routine” posts. I’m not sure which category this post belongs in. I won’t link to it.&lt;/p&gt;

&lt;p&gt;It showed a comparison between the outdated and the upgraded roadmaps for growth.&lt;/p&gt;

&lt;p&gt;The outdated roadmap was to find a job, work for 40 years, and retire. I completely agree. &lt;a href=&quot;/2025/05/07/LifeLesson/&quot;&gt;I wish I had learned that lesson 10 years ago&lt;/a&gt;. But the first step in the upgraded roadmap was to “Get clear on your goals in life.” Easier said than done!&lt;/p&gt;

&lt;p&gt;How do we “get clear on our goals”? Figuring out what we want in life is hard.&lt;/p&gt;

&lt;h2 id=&quot;an-easier-start-on-the-upgraded-roadmap&quot;&gt;An easier start on the upgraded roadmap&lt;/h2&gt;

&lt;p&gt;Last year, &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;I was burned out&lt;/a&gt; and I was laid off.&lt;/p&gt;

&lt;p&gt;The corporate world disappointed me. It squeezed all the passion I had until the very last drop. I was so lost that imagining a new life felt impossible.&lt;/p&gt;

&lt;p&gt;I still don’t know what I want in life. Maybe just waking up energized to work on something I love and &lt;a href=&quot;/2025/08/11/Present/&quot;&gt;being content and present&lt;/a&gt; every day.&lt;/p&gt;

&lt;p&gt;But what helped me recover from burnout and “get clear in life” was to focus on my physical and mental health. I didn’t know what to do next. No career plan, no new job. I only knew I didn’t want to stay the same.&lt;/p&gt;

&lt;p&gt;My reinvention phase started with a YouTube workout session, writing 200 words, and a moment of silence every day… And reading books from people who had gone through something similar, like Borja Vilaseca and &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;James’ books taught me that we don’t need a master goal or passion, whatever that means, to reinvent ourselves. We already have one goal, one passion: taking care of ourselves. That’s an easy place to start and maybe the most important one.&lt;/p&gt;

&lt;p&gt;Focusing on my health was the foundation of my reinvention after burning out. That’s why it became Idea #1 in &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=first-step-reinvent-your-life-you-feel-lost&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. A collection of tiny daily actions that actually change your life.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Surprising Lessons I Learned While Writing and Launching My First Book</title>
   <link href="https://canro91.github.io/2025/09/22/WritingABook/"/>
   <updated>2025-09-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/22/WritingABook</id>
   <content type="html">&lt;p&gt;I have a new writing challenge: I’m writing a book. One about &lt;a href=&quot;/2025/09/21/30Ways/&quot;&gt;how to improve our coding skills&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve had to &lt;a href=&quot;/2025/07/22/Books/&quot;&gt;change my mind about writing books&lt;/a&gt;. I used to think I needed a 10,000-word, fully-researched book. But reading &lt;a href=&quot;/2025/08/21/21Lessons/&quot;&gt;21 Lessons for the 21st Century&lt;/a&gt; showed me that a book could be a compilation of scattered ideas.&lt;/p&gt;

&lt;p&gt;Finishing the first draft is the beginning. Designing and selling it comes with their own challenges. Here are 7 lessons I’ve learned to design and sell a book so far:&lt;/p&gt;

&lt;h2 id=&quot;interior-design&quot;&gt;Interior design&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; Start every new chapter on a right-hand page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; You can design a book’s interior with Word or Google Docs. But &lt;a href=&quot;https://reedsy.com/studio/write-a-book&quot;&gt;Reedsy Studio&lt;/a&gt; automates a lot of the boring tasks.&lt;/p&gt;

&lt;p&gt;Interestingly, Reedsy only supports writing a book in English. You can change the spellchecker language, but I couldn’t find a way to change the copyright notice and front matter. So we’re back to Word for other languages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; The copyright page is boilerplate you can find online. Reedsy does it for us with a few clicks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Write your author page in the third person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5.&lt;/strong&gt; Invest in a professional cover design. People do judge a book by its cover.&lt;/p&gt;

&lt;h2 id=&quot;promoting-and-selling-with-amazon&quot;&gt;Promoting and selling with Amazon&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#6.&lt;/strong&gt; Amazon doesn’t require an ISBN for the Kindle and provides a free one for paperbacks. But you can’t use it elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7.&lt;/strong&gt; Pricing starting points:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;$0.99 is the new free&lt;/li&gt;
  &lt;li&gt;$2.99 for the Kindle version&lt;/li&gt;
  &lt;li&gt;$9.98 for the paperback version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;#8.&lt;/strong&gt; To follow Amazon’s rules, you can ask for reviews inside your book. But you can’t include external links to your book page.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Writing a New Book to Help You Improve Your Coding Skills</title>
   <link href="https://canro91.github.io/2025/09/21/30Ways/"/>
   <updated>2025-09-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/21/30Ways</id>
   <content type="html">&lt;p&gt;I’m celebrating a small victory today: I finished the 1st draft of my next coding book.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Its promise? 30 proven tips to get good at coding. Some are conventional. Some learned the hard way. And a few… weird ones. But all battle-tested.&lt;/p&gt;

&lt;h2 id=&quot;why-i-wrote-it&quot;&gt;Why I wrote it?&lt;/h2&gt;

&lt;p&gt;I went to a 5-year program to learn coding. Actually a bit longer because I had to write a stupid dissertation. But anyway…&lt;/p&gt;

&lt;p&gt;My real coding journey started with one Google search: “how to get good at coding.” I found lot of contradicting advice. That Google search put blogging under my radar. And that’s &lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;why I started my own blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It took me years and trial and error to find out what really works from all that free Internet advice.&lt;/p&gt;

&lt;p&gt;That’s why I wrote this book. Well, I’m still editing it. So you don’t have to go down the same rabbit hole.&lt;/p&gt;

&lt;p&gt;I wrote it for the younger me, sitting back at &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;my first job&lt;/a&gt; reading any tutorial he could find. Full of passion and no direction.&lt;/p&gt;

&lt;p&gt;These are the lessons I wish I had when I was starting out. The lessons I learned over 10 years of coding in tech companies and software agencies, making plenty of mistakes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Preorder your copy here&lt;/a&gt; to read a preview featuring 5 of the 30 tips, just to get you started.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Frustration of A Day Without Reception (I Wish I Were Analog)</title>
   <link href="https://canro91.github.io/2025/09/20/ADayWithoutReception/"/>
   <updated>2025-09-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/20/ADayWithoutReception</id>
   <content type="html">&lt;p&gt;I just had lunch. I’m standing in front of the counter to pay. But my bank app keeps loading.&lt;/p&gt;

&lt;p&gt;I toggle airplane mode to make my phone reconnect. That’s how a coder fixes anything, right? Restarting everything… No luck. I had no choice but to pay with the last bucks I had.&lt;/p&gt;

&lt;p&gt;With only two bars, I try to find the address of the closest Government office I need to visit. That’s why I had crossed the city. My browser shows a painfully slow progress bar. I only have two hours left before the office closes. It’s a Friday. This year the President passed an order to reduce the working schedule of all Government offices. I ask for directions. You know, anyone can get to Rome asking for directions, as we said in my hometown.&lt;/p&gt;

&lt;h2&gt;***&lt;/h2&gt;

&lt;p&gt;I make it to the office. Turns out, I wasn’t that far. It’s the first time I’ve done that errand. I need a copy of an official document. I explain what I need to the guy behind a protective glass. There are cameras on every corner. He tells me I need the document ID and the expedition date. I have them on my email. Oh, I can’t log in. Another loading animation. The guy tells me the major phone operator has had network issues all day long. He can’t use his phone either.&lt;/p&gt;

&lt;p&gt;With less than one hour left, I start to walk around. I remember an old Internet room from my college days nearby. After walking around three blocks, I find the place. It’s a small shop with four old PCs to access the Internet. It felt like the early 2000s, accessing my Messenger account to chat with my friends using funny animations. I could access my email account to get the ID and the issuance date of the document I wanted copied.&lt;/p&gt;

&lt;h2 id=&quot;-1&quot;&gt;***&lt;/h2&gt;

&lt;p&gt;I rush back to the Government office. I know I have to accept what Life sends me. But I’m not enjoying what I have in front of me. I slip a paper with the document ID and date under the protective glass. “These are the right numbers, but this wasn’t registered in this office.” Arrggg!&lt;/p&gt;

&lt;p&gt;All that chaos just because I didn’t have reception. All I needed was a yellow-page phone book, pocket money, and a notebook instead of &lt;a href=&quot;/2024/12/13/KeepingPhonesAround/&quot;&gt;a smartphone connected to the Internet&lt;/a&gt;. Maybe analog wasn’t that bad after all.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Programming deflation, story points, and vapers</title>
   <link href="https://canro91.github.io/2025/09/19/FridayLinks/"/>
   <updated>2025-09-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/19/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. With the raise of AI, we’re in a &lt;a href=&quot;https://tidyfirst.substack.com/p/programming-deflation&quot;&gt;programming deflation&lt;/a&gt; (5min). “We’re watching the basic economics of software development transform in real time.”&lt;/p&gt;

&lt;p&gt;#2. Typing isn’t (and hasn’t been) the bottleneck of coding. &lt;a href=&quot;https://etsd.tech/posts/coders-end/&quot;&gt;AI is making us thinkers instead of typers&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;#3. One of the things I hate about software engineering is estimation and story points. And guess what? &lt;a href=&quot;https://failfastmoveon.blogspot.com/2025/09/story-points-answer-nothing.html&quot;&gt;Story points answer nothing&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;#4. And here’s &lt;a href=&quot;https://bogdanthegeek.github.io/blog/projects/vapeserver/&quot;&gt;how someone hosted a website on a disposable vaper&lt;/a&gt; (6min). Cool!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/09/18/LegacyMigration/&quot;&gt;the hardest lesson I learned migrating legacy code (again)&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/09/15/ShouldIUseAI/&quot;&gt;the one rule junior coders should follow to adopt AI&lt;/a&gt; (1min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Hardest Lesson I Learned Migrating Legacy Code (Again)</title>
   <link href="https://canro91.github.io/2025/09/18/LegacyMigration/"/>
   <updated>2025-09-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/18/LegacyMigration</id>
   <content type="html">&lt;p&gt;I definitely can’t escape from legacy code.&lt;/p&gt;

&lt;p&gt;This time, it’s an old WebForms application written in VB. Yes, that old. It’s a family business. Dad started it. And years later, his two sons got involved. It’s funny when during the standup meetings I hear, “Hey Dad…”&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;The code is a mess. Sorry, I mean it needs a lot of care.&lt;/p&gt;

&lt;p&gt;I don’t blame them. It was the easy route with WebForms. Just drag and drop, double-click, and dump all your logic. It’s just like that framework encouraged people to copy and paste.&lt;/p&gt;

&lt;p&gt;I’ve worked with at least 3 legacy apps with WebForms. All of them were pure spaghetti code. &lt;a href=&quot;/2024/12/25/BestPractices/&quot;&gt;Zero best practices&lt;/a&gt;. “Coincidence? I don’t think so.” (Insert Toy Story character wearing a chicken suit)&lt;/p&gt;

&lt;p&gt;The code needs care, but the database needs even more attention.&lt;/p&gt;

&lt;p&gt;It doesn’t have foreign keys, and queries don’t use JOINs. If you want to join two tables, one of them has a string column with a list of IDs separated by a pipe (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12|34|56&lt;/code&gt;). You fetch records separately and then loop through them to join them. Arrggg!&lt;/p&gt;

&lt;p&gt;And please, let’s not talk about stored procedures…&lt;/p&gt;

&lt;h2 id=&quot;the-lesson&quot;&gt;The lesson&lt;/h2&gt;

&lt;p&gt;But from this project I’ve learned that if we don’t pay close attention to the initial database design, the backend side gets wildly crazy. A lot of hacks and patches to make up for a poor DB design.&lt;/p&gt;

&lt;p&gt;There’s no need to go crazy with database normalization. Just:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Every table should have an auto-incremented ID.&lt;/li&gt;
  &lt;li&gt;Use a separate table instead of a column with a list of IDs.&lt;/li&gt;
  &lt;li&gt;Keep data types consistent across tables. All “Status” columns should be either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BIT&lt;/code&gt; or an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INT&lt;/code&gt;. Otherwise, &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;conversions could go wild&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/12/11/AuditFieldsWithOrmLite/&quot;&gt;Add audit fields&lt;/a&gt; (“createdAt” and “updatedAt”) to every table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wish they had followed those simple guidelines 10 years ago.&lt;/p&gt;

&lt;p&gt;But more surprisingly, I learned that you don’t need a perfectly clean codebase to have a successful business. You will suffer? Sure. But running a business and writing good code are two completely separate skill sets.&lt;/p&gt;

&lt;p&gt;The freshly graduated me thought I was only going to always create systems from scratch following all principles and practices to the tee. But working with legacy code taught me more than any coding course.&lt;/p&gt;

&lt;p&gt;That’s why I made it one of the 30 proven strategies in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=hardest-lesson-i-learned-migrating-legacy-code&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Simplest Way to Validate a Product Idea (Without Wasting Time and Money)</title>
   <link href="https://canro91.github.io/2025/09/17/ValidateIdeas/"/>
   <updated>2025-09-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/17/ValidateIdeas</id>
   <content type="html">&lt;p&gt;A long time ago, a friend asked me for a quote on a custom piece of software.&lt;/p&gt;

&lt;p&gt;She and her friend wanted to start a marketing agency. And they wanted to build custom software to run it.&lt;/p&gt;

&lt;p&gt;The problem? They didn’t have any savings or capital. And even worse, they didn’t have anyone to sell their service to. It was too early to build software.&lt;/p&gt;

&lt;p&gt;I’ve been there too, on a different scale, coming up with value propositions and website copy for a freelance business I didn’t have yet. Later, &lt;a href=&quot;/2025/02/20/HourlyBillingIsNuts/&quot;&gt;it died with a single phone call&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After immersing myself in business podcasts and books, I’ve learned to:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Come up with simple and cheap experiments to test a business idea&lt;/li&gt;
  &lt;li&gt;Slap a price tag on whatever we’re building to see who buys&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And simple experiments can validate big ideas.&lt;/p&gt;

&lt;p&gt;The other day I watched &lt;a href=&quot;https://www.youtube.com/watch?v=HSVbD7RhOHU&quot;&gt;a Diary of a CEO episode featuring a Netflix cofounder&lt;/a&gt;. How they validated their idea? He mailed a CD to his business partner to see if they could run a Blockbuster alternative by email. It was a cheap experiment. It worked.&lt;/p&gt;

&lt;p&gt;Run &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;experiments to learn&lt;/a&gt;, to grow, and to build something people really want.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Content Experiments I Ran in August—and What Happened</title>
   <link href="https://canro91.github.io/2025/09/16/ContentExperiments/"/>
   <updated>2025-09-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/16/ContentExperiments</id>
   <content type="html">&lt;p&gt;Back in August, I shared &lt;a href=&quot;/2025/08/03/ContentExperiments/&quot;&gt;7 content experiments&lt;/a&gt; I wanted to try.&lt;/p&gt;

&lt;p&gt;As part of my daily routine, &lt;a href=&quot;/2025/06/17/TenIdeas/&quot;&gt;I write 10 bad ideas&lt;/a&gt; every day to keep my creativity muscles in shape. I wrote 10 ideas that day, but only posted 7.&lt;/p&gt;

&lt;p&gt;I acted on some of those ideas. Here’s what I actually did:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. I redesigned the interior of my C# Idioms book.&lt;/strong&gt; For the first version, I used Canva. Nothing wrong with Canva. But I wanted a more professional, less hobbyist design. So I added a copyright notice, an introduction, a table of content. The basics of a publishable book. Now, &lt;a href=&quot;https://imcsarag.gumroad.com/l/10csharpidioms&quot;&gt;it’s available for $0.99&lt;/a&gt;. Next move? Offer it via Amazon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. I uploaded all my coding courses to Gumroad.&lt;/strong&gt; I have to admit the experience of Gumroad as a student isn’t as polished as Udemy for video courses. But it works well enough to show a video lesson with a description. Now, they’re available here: &lt;a href=&quot;https://imcsarag.gumroad.com/l/csharpnullreferenceexceptiondemystified&quot;&gt;C# NullReferenceException Demystified&lt;/a&gt;, &lt;a href=&quot;https://imcsarag.gumroad.com/l/gettingstartedwithlinq&quot;&gt;Getting Started With LINQ in C#&lt;/a&gt;, and &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting201&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. I packaged all my beginner’s material into the “C# Fundamentals Bundle.”&lt;/strong&gt; That’s an ebook and more than 2 hours of video lessons to learn C#, at 20% off. It’s available here: &lt;a href=&quot;https://imcsarag.gumroad.com/l/csharpfundamentalsbundle&quot;&gt;C# Fundamentals Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After launching these, I notice a challenge: Gumroad doesn’t have a powerful discoverability feature. As creators, we can’t simply rely on Gumroad showing our products to users. At least, we shouldn’t expect it. We have to redirect traffic there ourselves. Now, my only source of traffic is &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;my newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. I changed all prices to end in .99 and increased the suggested prices.&lt;/strong&gt;, I offer my books and courses as “Pay what you want.” I’ve received $1, $5, $7, $10, and $20 as payments. So I changed my current prices based on those anchor points.&lt;/p&gt;

&lt;p&gt;Results? 10 free downloads and no sales. Not that bad. I learned about interior book design and practiced my copywriting skills. That’s helpful for any future project.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Junior Coders: The One Rule You Should Follow to Adopt AI</title>
   <link href="https://canro91.github.io/2025/09/15/ShouldIUseAI/"/>
   <updated>2025-09-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/15/ShouldIUseAI</id>
   <content type="html">&lt;p&gt;&lt;em&gt;“Is using AI totally forbidden?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“When and how should I use it?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“What’s your take on vibecoding?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was on &lt;a href=&quot;/2025/09/11/ALXSept2025/&quot;&gt;a firechat with a community of new coders&lt;/a&gt; last week. Those questions kept popping up. They all wanted to know about AI.&lt;/p&gt;

&lt;p&gt;I get it! There’s a lot of noise:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/25/WillAITakeMyJob/&quot;&gt;AI is taking our jobs&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“%X of code is generated by AI at $BigCorp.”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Some guy shared on Twitter/X that he built an app with no coding skills and now he’s retired, drinking martinis in the Caribbean.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t be discouraged by those headlines. (There’s a lot of nuance behind them.) &lt;a href=&quot;/2025/08/18/TooLateToLearnCoding/&quot;&gt;We’re living in the best time to learn coding&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;you-wont-like-it-but&quot;&gt;You won’t like it, but…&lt;/h2&gt;

&lt;p&gt;Here’s the rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t use AI to generate code until you’re comfortable coding on your own.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you only copy and paste what ChatGPT, Cursor, or &lt;em&gt;$NewestFastestLLM&lt;/em&gt; gives you, &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;you’re in trouble&lt;/a&gt;. You need to know if what that tool is spitting out is good code. And for that, you need your own judgment.&lt;/p&gt;

&lt;p&gt;When in doubt, think of &lt;a href=&quot;/2025/03/31/AIAndCalculators/&quot;&gt;AI as a powerful calculator in math class&lt;/a&gt;. It makes you faster, but you still need to know how to solve equations. The thinking part is still yours.&lt;/p&gt;

&lt;p&gt;AI can spit out code in seconds, even with a bad prompt. But coding is more than syntax. It’s also about teamwork, clear communication, and problem solving. 
That’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;, a practical guide to the skills that actually make you a better coder. It’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=junior-coders-one-rule-follow-adopt-ai&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>3 Health Habits to Live Longer—from the Man Trying to Live Forever</title>
   <link href="https://canro91.github.io/2025/09/14/LivingForever/"/>
   <updated>2025-09-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/14/LivingForever</id>
   <content type="html">&lt;p&gt;His name is Bryan Johnson and he has one single obsession: to stop and reverse his aging.&lt;/p&gt;

&lt;p&gt;His obsession has led him to turn his apartment into a $2.5M science lab. He measures and tracks hundreds of markers to check if he’s getting closer to his goal.&lt;/p&gt;

&lt;p&gt;He has the discipline of an Olympic athlete: eats only 2000 calories, takes 111 pills, and works out for one hour. Every. Single. Day.&lt;/p&gt;

&lt;p&gt;He’s 45. But he’s aging as fast as a 10-year-old boy. (We don’t think of aging when we imagine a young boy). He beats his teenage children in almost any sport and aces any fitness competition with straight A’s.&lt;/p&gt;

&lt;p&gt;His goal has attracted the media attention. Netflix has a documentary about him: &lt;a href=&quot;https://www.netflix.com/co-en/title/81757532&quot;&gt;Don’t Die&lt;/a&gt;. And you can find plenty of interviews on YouTube.&lt;/p&gt;

&lt;p&gt;I watched his documentary and &lt;a href=&quot;https://youtu.be/YNsbvVwN6zk&quot;&gt;the interview on the James Altucher Show&lt;/a&gt;, here’s what I learned:&lt;/p&gt;

&lt;h2 id=&quot;1-identify-your-self-destructing-behaviors&quot;&gt;#1. Identify your self-destructing behaviors&lt;/h2&gt;

&lt;p&gt;The Big Mac and the 2-hour binge-watching on Netflix after work are coping mechanisms to run away from the life we hate.&lt;/p&gt;

&lt;p&gt;That’s your mind looking for forms of pleasure. Learn to identify the self-destructing self and separate mentally from them. &lt;em&gt;“Here it comes, the destructive Alice. Hi! I don’t need you around.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That was Bryan’s first step before embarking on his goal of eternal life.&lt;/p&gt;

&lt;h2 id=&quot;2-sleep-8-hours-and-eat-well&quot;&gt;#2. Sleep 8 hours and eat well&lt;/h2&gt;

&lt;p&gt;If you don’t have $2.5M to turn your apartment into a lab, start taking care of your sleep and diet.&lt;/p&gt;

&lt;p&gt;Here’s Bryan’s 80/20 for wellness:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Go to bed at the same time.&lt;/li&gt;
  &lt;li&gt;No light in your bedroom.&lt;/li&gt;
  &lt;li&gt;No alcohol.&lt;/li&gt;
  &lt;li&gt;Eat less.&lt;/li&gt;
  &lt;li&gt;Eat more nutritious foods.&lt;/li&gt;
  &lt;li&gt;Don’t eat before going to bed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t need plasma transfusions like Bryan. Rest and pay attention to what you put into your body.&lt;/p&gt;

&lt;h2 id=&quot;3-live-a-steady-life&quot;&gt;#3. Live a steady life&lt;/h2&gt;

&lt;p&gt;Be like a high-performance athlete: focused on your body and mind.&lt;/p&gt;

&lt;p&gt;Protect your mind by staying away from cheap dopamine like Candy Crush, news, social media, and smartphones. And, don’t use screens before going to bed. It ruins your sleeping patterns too.&lt;/p&gt;

&lt;p&gt;After following his regimen for two years, Bryan has changed so much Face ID has had a hard time recognizing him. Maybe you don’t want to adopt a strict lifestyle to live forever, but if you want to age well, start today, not at 70 when it’s too late.&lt;/p&gt;

&lt;p&gt;Well, it turns out I’m not that far with &lt;a href=&quot;/2025/05/01/AvoidDoctors/&quot;&gt;my healthy habits&lt;/a&gt;. I’m only 45 extra minutes of exercise, 99 pills, and a science lab away. But hey, we all start somewhere.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A 4-Step Framework (for Podcasts and Interviews) To Answer Questions Like a Pro</title>
   <link href="https://canro91.github.io/2025/09/13/Answering/"/>
   <updated>2025-09-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/13/Answering</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;/2025/09/11/ALXSept2025/&quot;&gt;A recent podcast interview&lt;/a&gt; was the perfect excuse to ask for feedback on my interviewing skills.&lt;/p&gt;

&lt;p&gt;I’ve been putting more attention on &lt;a href=&quot;/2025/07/14/CommAtWork/&quot;&gt;my communication skills&lt;/a&gt;, but never asked for feedback. So I reached out to &lt;a href=&quot;https://www.linkedin.com/in/ryan-wiens/&quot;&gt;Ryan Alexander Wiens&lt;/a&gt;, a coach who helps engineers speak clearly and show impact, showing him one of my answers.&lt;/p&gt;

&lt;p&gt;Here are two suggestions Ryan gave me. I’m sure he doesn’t mind I’m sharing them here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Slow down at key words and pause for emphasis.&lt;/strong&gt; It’s hard to sound confident while answering well. Pauses give us time to think and create a sense of expectation for the listeners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Answer with PREP: Point/Reason/Example/Point.&lt;/strong&gt; To answer questions confidently, I learned to start and end with the main point.&lt;/p&gt;

&lt;p&gt;But to make our answers more impactful, add a “Why” or a reason after the main point. Then to end the answer, restate the question with the main point again. We tend to remember only the beginning and end, so win-win! We look more confident and help the listener remember our answer.&lt;/p&gt;

&lt;p&gt;So let’s say we’re asked about the mistakes beginners make in terms of branding. Following the 4-step framework we could say:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(Point) I’d say one of my biggest mistakes has been &lt;pause&gt; not to start earlier. &lt;pause&gt;&lt;/pause&gt;&lt;/pause&gt;&lt;/p&gt;

  &lt;p&gt;(Reason) Building a brand has brought me great opportunities, like this interview… I wish I had started sooner.&lt;/p&gt;

  &lt;p&gt;(Example) If I had to start right from scratch again, I’d…&lt;/p&gt;

  &lt;p&gt;(Point) That was my mistake and the mistake most beginners make: waiting too long to start.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now thanks to Ryan’s PREP framework, you’re PREPared for your next podcast or job interview. Pun intended.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Programming pearls, hating AI, and formatting</title>
   <link href="https://canro91.github.io/2025/09/12/FridayLinks/"/>
   <updated>2025-09-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/12/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Before the usual 4 links, a quick update about my book.&lt;/p&gt;

&lt;p&gt;This week, I finished another four chapters. But there’s still work to do.&lt;/p&gt;

&lt;p&gt;I haven’t chosen a final book title, but what about &lt;em&gt;30 Ways to Get Better at Coding&lt;/em&gt;? What do you think? Does it sound interesting?&lt;/p&gt;

&lt;p&gt;And… Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. Forty years ago, Programming Pearls was released, a collection of hard truths about coding. But are they still relevant? Here’s &lt;a href=&quot;https://shkspr.mobi/blog/2025/09/40-years-later-are-bentleys-programming-pearls-still-relevant/&quot;&gt;a breakdown of those pearls&lt;/a&gt; (20min).&lt;/p&gt;

&lt;p&gt;#2. Here’s &lt;a href=&quot;https://malwaretech.com/2025/08/every-reason-why-i-hate-ai.html&quot;&gt;why the guy who stopped the largest cyberattack in history hates AI&lt;/a&gt; (30min).&lt;/p&gt;

&lt;p&gt;#3. Want to understand the AI hype? &lt;a href=&quot;https://idiallo.com/blog/ai-is-a-subscription-company&quot;&gt;It’s not a technology, but a subscription company&lt;/a&gt; (6min).&lt;/p&gt;

&lt;p&gt;#4. Speaking of coding in the old days, here’s &lt;a href=&quot;https://maxleiter.com/blog/formatting&quot;&gt;how ADA handled code formatting&lt;/a&gt; (4min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, this week I was invited to a firechat session with the LAX Africa community to discuss how to land a first job. Here’s &lt;a href=&quot;/2025/09/11/ALXSept2025/&quot;&gt;a summary of the session&lt;/a&gt; (5min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>15 Takeaways From &quot;Breaking in the Mindset That Gets You Hired&quot; With ALX</title>
   <link href="https://canro91.github.io/2025/09/11/ALXSept2025/"/>
   <updated>2025-09-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/11/ALXSept2025</id>
   <content type="html">&lt;p&gt;This week, I had the chance to share some of my career lessons with the &lt;a href=&quot;https://www.facesofalxse.com/&quot;&gt;ALX Africa community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I joined &lt;a href=&quot;https://www.shehababdelsalam.com/&quot;&gt;Shehab Abdel-Salam&lt;/a&gt;, a Senior Software Engineer at Proofpoint, to share the mindset shifts needed to land a coding job for the first time.&lt;/p&gt;

&lt;p&gt;Here’s the recording of the session—In case you want to watch it, there’s some back jokes:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/NOO66OORShU?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;And here are 15 takeaways from the session—In case you don’t want to watch the recording:&lt;/p&gt;

&lt;h2 id=&quot;career-growth&quot;&gt;Career Growth&lt;/h2&gt;

&lt;h3 id=&quot;1-identify-your-gray-zones-vs-growth-zones&quot;&gt;#1. Identify your gray zones vs growth zones.&lt;/h3&gt;

&lt;p&gt;A gray zone is doing comfortable work.
And a growth zone is doing work that stretches your skills.&lt;/p&gt;

&lt;p&gt;To grow your career, do the things that scare you. Comfort zones kill growth.&lt;/p&gt;

&lt;h3 id=&quot;2-forget-the-corporate-ladder&quot;&gt;#2. Forget the corporate ladder.&lt;/h3&gt;

&lt;p&gt;Hard work alone doesn’t guarantee results.&lt;/p&gt;

&lt;p&gt;Instead of chasing the corporate ladder, define your own success metrics and climb your own ladder.&lt;/p&gt;

&lt;h3 id=&quot;3-stand-out-at-work-by-doing-the-work-nobody-else-wants-to-do&quot;&gt;#3. Stand out at work by doing the work nobody else wants to do.&lt;/h3&gt;

&lt;p&gt;And make sure you’re able to do it.&lt;/p&gt;

&lt;p&gt;By the way, that’s only one way to stand out besides hard work. &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;Here are another 9&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;4-be-aware-of-cultural-expectations-when-working-remotely&quot;&gt;#4. Be aware of cultural expectations when working remotely.&lt;/h3&gt;

&lt;p&gt;Coming from LatAm, when I started working with American companies, I missed the chitchat and off-topic conversations before starting meetings. The American way is direct and to the point.&lt;/p&gt;

&lt;h3 id=&quot;5-as-a-junior-coder-stand-out-by-showing-youre-able-to-learn-new-subjects-and-follow-instructions&quot;&gt;#5. As a junior coder, stand out by showing you’re able to learn new subjects and follow instructions.&lt;/h3&gt;

&lt;p&gt;As a senior coder, it’s the opposite. You stand out by showing you don’t need many instructions.&lt;/p&gt;

&lt;h3 id=&quot;6-rely-on-your-personal-and-professional-network-to-look-for-your-first-job&quot;&gt;#6. Rely on your personal and professional network to look for your first job.&lt;/h3&gt;

&lt;p&gt;Shake hands online and offline and skip the hiring lines.&lt;/p&gt;

&lt;p&gt;Over ten years ago, I didn’t apply through a job portal to &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;land my first job&lt;/a&gt;. I knew someone who knew someone who made an introduction. Then when I left my first job (fired actually), my ex-boss arranged an interview for me. That’s the power of your network.&lt;/p&gt;

&lt;p&gt;It sounds like a cliche, but &lt;em&gt;your network is your net worth&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;7-listen-to-feedback-say-thanks-and-act-on-it&quot;&gt;#7. Listen to feedback, say thanks, and act on it.&lt;/h3&gt;

&lt;p&gt;Avoid the temptation of explaining and justifying your behavior.&lt;/p&gt;

&lt;h2 id=&quot;writing&quot;&gt;Writing&lt;/h2&gt;

&lt;h3 id=&quot;8-writing-online-is-one-of-the-most-rewarding-skills-for-your-career&quot;&gt;#8. Writing online is one of the most rewarding skills for your career.&lt;/h3&gt;

&lt;p&gt;It improves your research and communication skills.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;my blog has opened many career opportunities&lt;/a&gt;. Thanks to a link to my blog on my CV, I turned a failed interview into a content collaboration… And I made some lunch money.&lt;/p&gt;

&lt;h3 id=&quot;9-your-writing-and-online-presence-can-replace-your-portfolio&quot;&gt;#9. Your writing and online presence can replace your portfolio.&lt;/h3&gt;

&lt;p&gt;Every time you finish a project (or move to another job or achieve a milestone), write about the lessons you learned and what you would have done differently. And showcase those posts in &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;your LinkedIn profile&lt;/a&gt; or CV.&lt;/p&gt;

&lt;h3 id=&quot;10-if-youre-completely-new-to-writing-start-with-aworklog&quot;&gt;#10. If you’re completely new to writing, start with a worklog.&lt;/h3&gt;

&lt;p&gt;If you have only written README files for your GitHub repos, you don’t need to write deep dives.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/04/10/TilPosts/&quot;&gt;Start with “Today I Learned” posts&lt;/a&gt;. That’s exactly how I started writing. My very first post ever was a word vomit pretending to be a coding tutorial about Aspect-Oriented Programming in C#. (I’m so embarrassed by that post, but I still keep unedited to remind me how I started).&lt;/p&gt;

&lt;p&gt;Document what you’re learning and the resources you’re using. That’s the easiest way to start writing.&lt;/p&gt;

&lt;h2 id=&quot;technical-skills&quot;&gt;Technical Skills&lt;/h2&gt;

&lt;h3 id=&quot;11-build-simple-apps-and-projects-to-practice&quot;&gt;#11. Build simple apps and projects to practice.&lt;/h3&gt;

&lt;p&gt;Or even clone existing apps and some of their features.&lt;/p&gt;

&lt;h3 id=&quot;12-understand-you-dont-need-many-programming-languages-to-be-a-good-coder&quot;&gt;#12. Understand you don’t need many programming languages to be a good coder.&lt;/h3&gt;

&lt;p&gt;You can make your way with HTML/CSS/Javascript, one backend language (JavaScript counts here), and SQL.&lt;/p&gt;

&lt;h3 id=&quot;13-read-engineering-blogs&quot;&gt;#13. Read engineering blogs:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.joelonsoftware.com/&quot;&gt;Joel on Software&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://newsletter.pragmaticengineer.com/&quot;&gt;The Pragmatic Engineer&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://alifeengineered.substack.com/&quot;&gt;A Life Engineered&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://proactiveengineer.com/&quot;&gt;Shehab’s blog&lt;/a&gt;, and&lt;/li&gt;
  &lt;li&gt;Mine, of course.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;14-embrace-the-struggle&quot;&gt;#14. Embrace the struggle.&lt;/h3&gt;

&lt;p&gt;It’s part of the learning process.&lt;/p&gt;

&lt;p&gt;When you’re stuck with a coding problem, don’t rush to AI for a quick answer. Try solving it yourself and don’t hesitate to ask for help.&lt;/p&gt;

&lt;h3 id=&quot;15-dont-be-scared-of-ai&quot;&gt;#15. Don’t be scared of AI.&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://canro91.github.io/2025/03/24/NewCodersAndAI/&quot;&gt;Use it wisely&lt;/a&gt;. Otherwise, your coding muscles could atrophy.&lt;/p&gt;

&lt;p&gt;If you’re starting out, keep learning and having fun. &lt;a href=&quot;/2025/08/18/TooLateToLearnCoding/&quot;&gt;This is the best time to learn coding&lt;/a&gt;. Always be a beginner.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>This Simple Word Change to Reveal What AI Really Means</title>
   <link href="https://canro91.github.io/2025/09/10/WhatAIReallyMeans/"/>
   <updated>2025-09-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/10/WhatAIReallyMeans</id>
   <content type="html">&lt;p&gt;AI is the new buzzword of the day.&lt;/p&gt;

&lt;p&gt;Add “AI” to a company or product name to join the hype. It’s all over the news. &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;AI is the new contender for coders&lt;/a&gt;. Yes, we coders are always the ones being replaced.&lt;/p&gt;

&lt;p&gt;But Ibrahim Diallo made a good point in his post &lt;a href=&quot;https://idiallo.com/blog/ai-is-a-subscription-company&quot;&gt;AI is Not a Technology, It’s a Subscription Company&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Swap “AI” for “subscription company.” And &lt;a href=&quot;/2024/12/08/CEOVsJanitor/&quot;&gt;all AI those headlines&lt;/a&gt; make more sense. &lt;em&gt;“%X of code at $BigTech is generated by a $SubscriptionCompany”&lt;/em&gt; has a new meaning.&lt;/p&gt;

&lt;p&gt;AI isn’t a product we own. It’s subscription companies profiting off our data. And that isn’t innovative or disruptive at all.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>4 Tips to Avoid Rushing to Code (and Building the Wrong Thing)</title>
   <link href="https://canro91.github.io/2025/09/09/RushingToCode/"/>
   <updated>2025-09-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/09/RushingToCode</id>
   <content type="html">&lt;p&gt;You spent days on your JIRA ticket… only to be told to redo it after your team lead reviewed your code?&lt;/p&gt;

&lt;p&gt;A few years ago, I was working on a hotel management tool. My team lead asked me to redo an apparently trivial task. I had to store emails before sending them. It wasn’t a full rework, but I had to change my approach. We had completely different expectations from the same task. Two days of work almost wasted.&lt;/p&gt;

&lt;p&gt;If I had only asked one single question before starting… “Hey, I’m doing it like this, are we on the same page?”&lt;/p&gt;

&lt;p&gt;If you’re like me, eager to jump into the code, confident in your solution, hold your horses and follow these four tips:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Always ask why.&lt;/strong&gt; Don’t start coding if you don’t understand what needs to be done. Ask: Why this task? What’s the real problem? Why solve it now?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Read the existing code before starting.&lt;/strong&gt; Your changes might be simple, unless you have to refactor some legacy code first. If you rush to code without knowing that, you’ll give the wrong impression you’re taking too long on a simple task. Yes, &lt;a href=&quot;/2025/04/17/HarshTruths/&quot;&gt;estimates are hard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Outline your solution with comments.&lt;/strong&gt; Start by sketching your plan in comments. That’s your blueprint. Of course, once you’re done, don’t forget to delete them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Write a one-page spec.&lt;/strong&gt; Draft a summary of the changes you need to implement, just for yourself. It’s for you to &lt;a href=&quot;/2024/11/08/WhyWriting/&quot;&gt;think clearly&lt;/a&gt; before writing a single line of code.&lt;/p&gt;

&lt;p&gt;A simple question would have saved me from wasting two days of work. Make sure everyone agrees on your solution before you start. It could save you from building the wrong thing.&lt;/p&gt;

&lt;p&gt;It’s better to annoy people by asking too many questions than by making mistakes for not asking any questions at all. Strive for context before coding. Always.&lt;/p&gt;

&lt;p&gt;I cover 30 lessons like the one in this post in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I had on my journey from junior to senior.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=tips-avoid-rushing-code-building-wrong-thing&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;—Because typing isn’t the hardest part of coding, but knowing what to type.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>8 Reading Habits I Learned from Ryan Holiday, the Stoic Book Master</title>
   <link href="https://canro91.github.io/2025/09/08/StoicBookReading/"/>
   <updated>2025-09-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/08/StoicBookReading</id>
   <content type="html">&lt;p&gt;I’ve been binge-watching Ryan Holiday’s Daily Stoic YouTube channel lately.&lt;/p&gt;

&lt;p&gt;If you don’t know about his work, he reads and writes books for a living. He wrote &lt;em&gt;The Obstacle is the Way&lt;/em&gt; and &lt;em&gt;Life of the Stoics&lt;/em&gt;. And just the other day, I watched &lt;a href=&quot;/2025/09/07/JoeRogan/&quot;&gt;his appearance on Joe Rogan’s show&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After watching his videos with reading advice, here are 8 lessons I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Read physical books.&lt;/strong&gt; That’s an excuse to spend less time in front of screens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Prefer old books.&lt;/strong&gt; Focus on books that have stood the test of time. If they have survived this long, they will survive a whole lot more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Reread.&lt;/strong&gt; &lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;I had to change my mind about rereading&lt;/a&gt;. I was against it when I tried to grow a large list of books I’d read. When we reread a book, our circumstances have changed. Every time we revisit a book, it’s an opportunity to learn or notice new insights.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Have mentors to point you to more books.&lt;/strong&gt; If you can’t find one, &lt;a href=&quot;/2024/11/26/FindingMentors/&quot;&gt;find a not-mentor&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. “Not all readers are leaders, but all leaders are readers.”&lt;/strong&gt; That’s a quote attributed to Harry Truman, one of the U.S. presidents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Don’t speed read.&lt;/strong&gt; To read faster, we need to read more. Simple as that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Interact with the book you’re reading.&lt;/strong&gt; Reading a book is like a conversation with the author. Highlight, fold corners, and take notes in the margins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Use notecards for notes.&lt;/strong&gt; After a break, go back to the parts you highlighted or the pages you folded, and turn those interesting passages into notes. Ryan’s note-taking system sounds like &lt;a href=&quot;/2020/11/18/HowToTakeSmartNotes/&quot;&gt;Luhmann’s Zettelkasten method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you want to read more, here are &lt;a href=&quot;/2025/04/28/OneBookAWeek/&quot;&gt;8 easy-to-implement tips to read one book a week&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Simple Rule Behind Joe Rogan&apos;s Podcast Success</title>
   <link href="https://canro91.github.io/2025/09/07/JoeRogan/"/>
   <updated>2025-09-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/07/JoeRogan</id>
   <content type="html">&lt;p&gt;I’ve been binge-watching Ryan Holiday’s videos.&lt;/p&gt;

&lt;p&gt;YouTube took me to his appearance on the &lt;a href=&quot;https://youtu.be/_PMesNbFuFQ&quot;&gt;Joe Rogan Experience&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I watched the whole 2-hour video over a couple of days. It’s long and not always about stoicism and books. But there was a gem I found really inspiring.&lt;/p&gt;

&lt;p&gt;Joe shared his “secret:” he only invites guests he finds interesting and his only metric is if he does something engaging and entertaining for his audience.&lt;/p&gt;

&lt;p&gt;He started the podcast to meet interesting people. And even after it made money, he kept the same strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;I’ve been writing online for years&lt;/a&gt;. A big follower count seems attractive. But that interview reminded me to stay grounded. Do interesting things and have fun. Everything else is a plus.&lt;/p&gt;

&lt;p&gt;Oh, this reminded me of &lt;a href=&quot;/2025/06/30/CreativeProjects/&quot;&gt;the creative rules from the guys behind Field Notes&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Don&apos;t Learn Prompt Engineering. Here&apos;s What Matters More</title>
   <link href="https://canro91.github.io/2025/09/06/Prompting/"/>
   <updated>2025-09-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/06/Prompting</id>
   <content type="html">&lt;p&gt;I’m not an AI evangelist, and I’m not a hater either.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/05/24/CodingWithAI/&quot;&gt;I’ve tried AI for coding&lt;/a&gt;. And after a week or two, I noticed &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;how dependent I was becoming&lt;/a&gt;. Since then, I’ve used AI for small coding tasks, like generating small functions or finding typos, not to treat English as my main programming language.&lt;/p&gt;

&lt;p&gt;Marcus Hutchins, a security researches, puts it boldly in his post &lt;a href=&quot;https://malwaretech.com/2025/08/every-reason-why-i-hate-ai.html&quot;&gt;Every Reason Why I Hate AI and You Should Too&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I’d make a strong argument that what you shouldn’t be doing is “learning” to do everything with AI. What you should be doing is learning regular skills. Being a domain expert prompting an LLM badly is going to give you infinitely better results than a layperson with a “World’s Best Prompt Engineer” mug.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I agree with the core message. When everybody is relying on AI, it’s time to go back to old-school habits:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Read code&lt;/li&gt;
  &lt;li&gt;Write trustworthy tests&lt;/li&gt;
  &lt;li&gt;Devour classic textbooks&lt;/li&gt;
  &lt;li&gt;Troubleshoot bugs with pen and paper&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And outside coding: read books on paper, take notes by hand, and write our own summaries. To develop taste and judgment.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/03/31/AIAndCalculators/&quot;&gt;Using AI is like holding a calculator&lt;/a&gt; on a math exam. Even the best calculator is useless if you don’t know what to compute, it’s pretty much useless. Build skills, then leverage AI.&lt;/p&gt;

&lt;p&gt;I used to think coding was only about cracking symbols. That’s where AI shines. But coding is also about talking to non-tech managers, negotiating deadlines, and saying no politely.&lt;/p&gt;

&lt;p&gt;And that’s why I wrote, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;, to share the skills I wish I’d learned to become a confident coder.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=dont-learn-prompt-engineering-heres-what-matters&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;. It’s the roadmap I wish I had when I was starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Best engineers, AI failures, and simple things</title>
   <link href="https://canro91.github.io/2025/09/05/FridayLinks/"/>
   <updated>2025-09-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/05/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, quick update from my side before sharing the usual four links:&lt;/p&gt;

&lt;p&gt;I’m writing a book.&lt;/p&gt;

&lt;p&gt;Yes, a book. One with 30 lessons to get better at coding. Those are the lessons I wish I had on my way from Junior to Senior. I’ll share more details in future emails.&lt;/p&gt;

&lt;p&gt;OK, the four links as usual:&lt;/p&gt;

&lt;p&gt;#1. If &lt;a href=&quot;https://www.otherbranch.com/shared/blog/no-you-dont-want-to-hire-the-best-engineers&quot;&gt;every company wants to hire “the best engineers”&lt;/a&gt; (5min), who hires the rest of us?&lt;/p&gt;

&lt;p&gt;#2. AI can’t replace coders for real anytime soon. Here are &lt;a href=&quot;https://www.finalroundai.com/blog/vibe-coding-failures-that-prove-ai-cant-replace-developers-yet&quot;&gt;five AI screwups&lt;/a&gt; (12min) that prove we’re not there yet.&lt;/p&gt;

&lt;p&gt;#3. We shouldn’t blindly follow guidelines. Here’s a breakdown of one: &lt;a href=&quot;https://www.seangoedecke.com/the-simplest-thing-that-could-possibly-work/&quot;&gt;doing the simplest things&lt;/a&gt; (11min).&lt;/p&gt;

&lt;p&gt;#4. Here’s an interesting way of &lt;a href=&quot;https://www.davepagurek.com/blog/minimal-phone/&quot;&gt;repurposing an old phone into an ereader&lt;/a&gt; (15min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/09/04/FasterCodeReviews/&quot;&gt;two strategies for faster code reviews&lt;/a&gt; (3min) and &lt;a href=&quot;/2025/08/31/GoingToCollege/&quot;&gt;six pieces of advice I wish I had before going to college&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Two Proven Strategies For Faster Code Reviews (Learned From Dozens of Pull Requests)</title>
   <link href="https://canro91.github.io/2025/09/04/FasterCodeReviews/"/>
   <updated>2025-09-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/04/FasterCodeReviews</id>
   <content type="html">&lt;p&gt;Two days into my last full-time job, I thought I had finished my first task. I was wrong.&lt;/p&gt;

&lt;p&gt;At my previous job, I was used to going fast and breaking things. But that wasn’t the case anymore. After telling my team leader I was done, they ripped my code apart. I had to rewrite it almost entirely. No stored procedures. New naming conventions. &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit tests&lt;/a&gt;. That was my welcoming code review session.&lt;/p&gt;

&lt;p&gt;A couple of years after my first code review, I learned the unwritten rules and became one of the company’s “default” code reviewers. Yes, it took quite some time.&lt;/p&gt;

&lt;p&gt;As &lt;a href=&quot;/2022/12/19/LessonsAsReviewer/&quot;&gt;a default reviewer&lt;/a&gt;, I had the chance to review code from people outside my team. And after dozens of review sessions, I started to notice some patterns that made me change my reviewing strategy.&lt;/p&gt;

&lt;h3 id=&quot;1-follow-pull-request-driven-development&quot;&gt;1. Follow Pull-Request Driven Development&lt;/h3&gt;

&lt;p&gt;Occasionally, someone opened a pull request (PR) with thousands of lines.&lt;/p&gt;

&lt;p&gt;After noticing that, I adopted &lt;strong&gt;Pull-Request Driven Development&lt;/strong&gt; (PRDD). Before starting a task, think of how to split and organize your changes so they’re easy to review.&lt;/p&gt;

&lt;p&gt;Instead of a monstrous PR, split your changes into small, working PRs anyone can review in under five minutes.&lt;/p&gt;

&lt;p&gt;And if a task spans multiple projects, label and cross-reference each change for easier navigation. If you’re adding a new field to a form, create three separate PRs:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;PR 1/3 adds the new field&lt;/li&gt;
  &lt;li&gt;PR 2/3 updates the REST API&lt;/li&gt;
  &lt;li&gt;PR 3/3 changes the database schema.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can even use “PR #/3” as a title to make things easier for your reviewers.&lt;/p&gt;

&lt;h3 id=&quot;2-give-clear-and-actionable-feedback&quot;&gt;2. Give clear and actionable feedback&lt;/h3&gt;

&lt;p&gt;There was another pattern I noticed as a code reviewer: sometimes it took about 24 hours to get a pull request approved and merged.&lt;/p&gt;

&lt;p&gt;Some reviewers used &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;“pushy” questions&lt;/a&gt; that only hinted at a request for a change. Questions like &lt;em&gt;“What if this parameter is null?”&lt;/em&gt; weren’t clear enough. Was the reviewer asking for a change or simply starting a discussion? And since most of us were in different time zones, any interaction took about half or even an entire day.&lt;/p&gt;

&lt;p&gt;Those long code review sessions made me adopt three guidelines:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Write clear and unambiguous comments.&lt;/li&gt;
  &lt;li&gt;Always leave a suggestion with each comment.&lt;/li&gt;
  &lt;li&gt;Distinguish between actionable items and suggestions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That strategy turned a comment like &lt;em&gt;“What if this parameter is null?”&lt;/em&gt; into &lt;em&gt;“Is it possible that this parameter could be null? If that’s the case, please add the appropriate null checks.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I kept using that strategy inside my team, after my reviewer rotation ended. And I noticed how other reviewers picked it up as well. Point for preaching by example!&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting thought&lt;/h2&gt;

&lt;p&gt;Code reviews feel like a pain in the neck. I know!&lt;/p&gt;

&lt;p&gt;But until we put our code in front of others, we might think we’re writing the best code we’ve ever written, just like me before joining my last full-time job.&lt;/p&gt;

&lt;p&gt;Code reviews reveal how grumpy and opinionated we as coders can be when talking about code, arguing about variable names, code style, and best practices. Yes, often the stereotype is true.&lt;/p&gt;

&lt;p&gt;Sometimes you might want to remote choke your reviewers with the Force. But code reviews don’t just improve your code, they teach you to grow a thick skin and soften your ego.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>I&apos;m Bragging About a LinkedIn Recommendation That Made My Day</title>
   <link href="https://canro91.github.io/2025/09/03/Victories/"/>
   <updated>2025-09-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/03/Victories</id>
   <content type="html">&lt;p&gt;I don’t trust most LinkedIn recommendations.&lt;/p&gt;

&lt;p&gt;You can ask a friend or a coworker to say good things about you. And who would showcase bad comments for future employers to see? Nobody!&lt;/p&gt;

&lt;p&gt;But, but… Today I got a LinkedIn recommendation I didn’t ask for. A reader/follower wrote this recommendation:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;After reading his blog on SQL I connected with Cesar on LinkedIn , and his posts stood out for their clarity, honesty, and practical lessons. He has a ability to take real-world experiences in software engineering and turn them into valuable insights that others can learn from.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;When I reached out with a career-related question, Cesar didn’t just give me a quick reply, he went the extra mile and even wrote a full article to share his perspective. That generosity and willingness to help reflect the kind of person and communicator he is. Cesar’s writing and guidance have had a direct impact on the way I think about coding, career growth, and opportunities.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;I highly recommend Cesar for his exceptional writing, storytelling, and mentorship. He not only shares knowledge but also inspires others to take action and define their own paths.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s great to read that my words have inspired and impacted someone’s career. It means &lt;a href=&quot;/2025/08/26/RealWriting/&quot;&gt;my writing has done its job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Trying to hit the 300-post mark made me question if I should keep blogging. But that was a confirmation to keep doing it, even when it feels nobody is reading.&lt;/p&gt;

&lt;p&gt;That recommendation made my day. And that’s another screenshot I’m adding to &lt;a href=&quot;/2025/07/02/Victories/&quot;&gt;my “wins” folder&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What an Old Photo Album Taught Me About Tech</title>
   <link href="https://canro91.github.io/2025/09/02/Tech/"/>
   <updated>2025-09-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/02/Tech</id>
   <content type="html">&lt;p&gt;50 or 60 years ago, taking a picture was an event in and of itself.&lt;/p&gt;

&lt;p&gt;In the past weekend, I saw dozens of photos of my mom as a teenager and young woman. But one caught my attention.&lt;/p&gt;

&lt;p&gt;It was on her 7th birthday. She was dressed up. My grandparents took my uncle and her to the only photo studio in the city for a birthday photo. Then they took her for ice cream.&lt;/p&gt;

&lt;p&gt;Seeing that photo made me think about how photography used to work. The photographer had very few tries to get it right. They had to wait weeks to develop the film and see the final result.&lt;/p&gt;

&lt;p&gt;These days, we repeat the same shot as many times as we need until we get a decent picture. We tap our smartphones and see the results in seconds.&lt;/p&gt;

&lt;p&gt;That’s true for photography, writing, and pretty much anything else. Tech has shortened our feedback loops.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My 300 Daily Posts Reflection</title>
   <link href="https://canro91.github.io/2025/09/01/300DailyPosts/"/>
   <updated>2025-09-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/09/01/300DailyPosts</id>
   <content type="html">&lt;p&gt;This is my daily post #304.&lt;/p&gt;

&lt;p&gt;The 300-post mark felt hard to complete. I tried to balance multiple writing projects at once. I felt so tired at the end of the day that my writing streak almost suffer. And life threw me a huge unexpected curveball that made me miss a day. I ended up writing two posts the next day to recover.&lt;/p&gt;

&lt;p&gt;Finding ideas to write wasn’t the challenge. It was finding the time to write.&lt;/p&gt;

&lt;h2 id=&quot;my-most-read-and-favorite-posts&quot;&gt;My most-read and favorite posts&lt;/h2&gt;

&lt;p&gt;Here are some of the most-read posts from the last 100 days:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/06/28/AIRecovery/&quot;&gt;A Quick Recovery Guide for AI-Dependent Coders&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/07/17/TechBeforeBreakfast/&quot;&gt;10+ Pieces of Tech You Use Before Breakfast Without Even Realizing It&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/08/03/ContentExperiments/&quot;&gt;7 Fast Content Experiments I’m Running This Month (to Make a Buck or Two)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Six Proven Principles to Learn Any Skill Faster (Without Spending 10,000 Hours)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/06/12/WorstMoments/&quot;&gt;10 Life-Changing Lessons I Learned the Hard Way—from the Worst Moment of My Career&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are my &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;100-post&lt;/a&gt; and &lt;a href=&quot;/2025/05/20/200DailyPosts/&quot;&gt;200-post&lt;/a&gt; reflections.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Easiest Way to Preserve Our Stories</title>
   <link href="https://canro91.github.io/2025/08/31/Stories/"/>
   <updated>2025-08-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/31/Stories</id>
   <content type="html">&lt;p&gt;It’s been so relieving and therapeutic listening to many of my mom’s stories.&lt;/p&gt;

&lt;p&gt;My aunt pulled down an old photo album. I was full of joy seeing old photos of my mom as a teenager.&lt;/p&gt;

&lt;p&gt;The next day, my sister and I sat down to talk to my mom’s best friend. They meet at 3rd grade and were friends since then. I loved hearing all their child adventures, school days, and teenage loves.&lt;/p&gt;

&lt;p&gt;I was surprised by how many stories I heard.  Some of them I didn’t even know.&lt;/p&gt;

&lt;p&gt;Those two experiences reminded me that the best way to make an impact and preserve our stories is with a book. And for that, &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;we don’t need a best-seller&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Six Pieces of Advice I Wish I Had Known Before Going to College</title>
   <link href="https://canro91.github.io/2025/08/31/GoingToCollege/"/>
   <updated>2025-08-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/31/GoingToCollege</id>
   <content type="html">&lt;p&gt;I met a mom and her young girl about to start college. Like most teenagers, she was struggling to choose a major in college.&lt;/p&gt;

&lt;p&gt;I was no different. I didn’t know what to choose either. My problem was that I had too many options. I’ve struggle to &lt;a href=&quot;/2024/12/16/FindYourPassion/&quot;&gt;choose a single passion&lt;/a&gt; since always.&lt;/p&gt;

&lt;p&gt;I ended up picking the major the dislike the least.&lt;/p&gt;

&lt;p&gt;Here’s what I wish I had known before going to college:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Understand you don’t have to go to college. If decide not to do so, you’d have to come up with your own alternative plan. Maybe start a business or self educate.&lt;/li&gt;
  &lt;li&gt;Find an intersection between subjects you like and subjects with “employability” options. Again, understand you don’t have to follow the traditional path and go to a 9-5.&lt;/li&gt;
  &lt;li&gt;Don’t simply choose going to college and a particular major just because your parents did the same. Don’t simply go to med school, just because your dad is a doctor.&lt;/li&gt;
  &lt;li&gt;Learn as many skills you can. Among those, learn a manual skill: how to cut hair, make desserts…&lt;/li&gt;
  &lt;li&gt;Have as many options along side to going to college. Try to answer what if you don’t get accepted at XYZ college to major in ABC. What would you do then?&lt;/li&gt;
  &lt;li&gt;Understand you don’t have to stick to a single career for life. In this day and age, &lt;a href=&quot;/2024/11/20/Read500Books/&quot;&gt;we have to reinvent ourselves&lt;/a&gt; way more often than our parents.&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Developers&apos; block, databases, and APIs</title>
   <link href="https://canro91.github.io/2025/08/29/FridayLinks/"/>
   <updated>2025-08-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/29/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;I had a really busy, long, and heart-breaking week. I only could curate 3 links for you this week. Here they are.&lt;/p&gt;

&lt;p&gt;#1. Often, the hardest feeling for a writer is sitting in front of a blank page. That’s what we know as writers’ block. But what about &lt;a href=&quot;https://underlap.org/developers-block/&quot;&gt;developers’ block&lt;/a&gt; (8min).&lt;/p&gt;

&lt;p&gt;#2. Here’s a breakdown of &lt;a href=&quot;https://www.deepintodev.com/blog/how-databases-store-your-tables-on-disk&quot;&gt;how databases actually store data&lt;/a&gt; (16min).&lt;/p&gt;

&lt;p&gt;#3. Most of our time as backed coders is writing RESTful API, here’s &lt;a href=&quot;https://www.seangoedecke.com/good-api-design/&quot;&gt;how to do it&lt;/a&gt; (21min)&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/08/23/ISawItOnTiktok/&quot;&gt;how TikTok is a bad life coach&lt;/a&gt;. Not much coding this week on my blog&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check my &lt;a href=&quot;https://imcsarag.gumroad.com/l/csharpfundamentalsbundle&quot;&gt;C# Fundamentals Bundle&lt;/a&gt;, two easy-to-follow video courses and a practical ebook—now at a 20% discount. With the essential tools every new coder needs to start writing clean and professional-grade C# code.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Saddest Post I&apos;ve Ever Had to Write</title>
   <link href="https://canro91.github.io/2025/08/28/TheSaddestPost/"/>
   <updated>2025-08-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/28/TheSaddestPost</id>
   <content type="html">&lt;p&gt;This was supposed to be a 300-day post celebration. But it turned out to be the toughest post I ever had to write.&lt;/p&gt;

&lt;p&gt;After five years fighting with chronic kidney disease and two weeks in the hospital, one of the most important people in my life, my mom, passed away a few hours ago.&lt;/p&gt;

&lt;p&gt;I’m writing this with tears in my eyes and a deep hole in my soul. We all know these moments will come at some point, but nothing and no one prepares us for them.&lt;/p&gt;

&lt;p&gt;Today I want to remember mi mamá with a whole lot of gratitude.&lt;/p&gt;

&lt;p&gt;Thanks mom:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;For teaching me faith&lt;/li&gt;
  &lt;li&gt;For fighting so strong for so long&lt;/li&gt;
  &lt;li&gt;For showing me love and kindness&lt;/li&gt;
  &lt;li&gt;For being the best mom you could ever be&lt;/li&gt;
  &lt;li&gt;For sacrificing your career to raising me and my sister&lt;/li&gt;
  &lt;li&gt;For worrying about me even when you were sick in a bed too&lt;/li&gt;
  &lt;li&gt;For staying up with me to study through the night&lt;/li&gt;
  &lt;li&gt;For being there for me when I was sick&lt;/li&gt;
  &lt;li&gt;For all that you sacrificed for me&lt;/li&gt;
  &lt;li&gt;For the 62 years you were with us&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will always remember you, mamá. All that I am and what I have is because of you.&lt;/p&gt;

&lt;p&gt;In the loving memory of Rocío Romero, the best mom God, Life, and the Universe could have given me.&lt;/p&gt;

&lt;p&gt;We’ll see you again soon.&lt;/p&gt;

&lt;p&gt;PS: If heaven is real, there must be a welcoming party there today.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Failed to Follow This Writing Principle (and Didn&apos;t Get Some Blood)</title>
   <link href="https://canro91.github.io/2025/08/27/OneRuleOfWriting/"/>
   <updated>2025-08-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/27/OneRuleOfWriting</id>
   <content type="html">&lt;p&gt;These days a loved one needed a blood transfusion.&lt;/p&gt;

&lt;p&gt;My first instinct to look for donors was to write a WhatsApp status for my friends and contacts to see. I wrote something like,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Urgent! Blood donors needed. O+. DM for more info.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But then I realized I made the #1 mistake of writing and copywriting. I made the message about me, not about the readers. Anyone reading would ask, “What’s in there for me?” There was nothing for the reader.&lt;/p&gt;

&lt;p&gt;After realizing my mistake, I wore my copywriting hat and changed my message,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Has you done your good action of the day? Donate blood. It only takes a few minutes, you get a souvenir and have the chance of saving a life. DM me and I’d tell you how to help.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s a copy written in favor of the reader, not of the writer. Because good copy and good writing is always about the reader.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>There&apos;s No Such Thing As Real Writing. All Writing Is Real</title>
   <link href="https://canro91.github.io/2025/08/26/RealWriting/"/>
   <updated>2025-08-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/26/RealWriting</id>
   <content type="html">&lt;p&gt;For so long, I was afraid of putting “writer” on my bio online.&lt;/p&gt;

&lt;p&gt;I was full of self-doubt. “I don’t have thousands of followers.” “I don’t have a novel.” “I’m not even an English native speaker.”&lt;/p&gt;

&lt;p&gt;Even after &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;years of blogging&lt;/a&gt;, calling myself a writer was an impossible goal. Maybe because I pictured writers as Hemingways retreating to an island to return with a Nobel-winning novel.&lt;/p&gt;

&lt;p&gt;I wish I had read Mark Thompson’s post challenging the distinction between “real” writing and everything else sooner. It would have saved me so many moments of self-doubt. &lt;a href=&quot;https://seriousmarketersonly.medium.com/the-snobbery-of-real-writing-and-why-you-should-ignore-it-720d78147cc4&quot;&gt;He wrote&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;If your words help someone, teach someone, inspire someone, or even just get them to click “follow,” then they’ve done their job.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;If just one person has started writing and managed to change their situation after reading my articles, that is a win.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stop chasing the “real writer” myth.&lt;/p&gt;

&lt;p&gt;If you have helped, inspired, or made someone take any form of action with your words, that’s real writing. It doesn’t matter if it’s a Tweet, a blog post, or a 70,000-word work of fiction. Because all writing that moves someone is real. Period.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Good Ideas Aren&apos;t Enough—You Need Execution Ideas Too</title>
   <link href="https://canro91.github.io/2025/08/25/ExecutionIdeas/"/>
   <updated>2025-08-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/25/ExecutionIdeas</id>
   <content type="html">&lt;p&gt;Since last year, I’ve made a habit to write 10 ideas a day.&lt;/p&gt;

&lt;p&gt;That’s a concept I learned from &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;. I even followed some of the exercises from “How to Become an Idea Machine,” a book with dozens of prompts to exercise your idea muscles.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;Writing those 10 ideas&lt;/a&gt; has taught me we need plenty of bad ideas to find a good one. And once we find a good idea, we need ideas to execute it.&lt;/p&gt;

&lt;p&gt;Did you come up with &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;a book idea&lt;/a&gt;? Great. The next step is writing another 10 to (possibly) make that book a reality. Do you need research? Do you need to compile scattered blog posts? Do you need to test subjects first on social media?&lt;/p&gt;

&lt;p&gt;This applies beyond writing books. These days, a neighbor decided to change our main door lock. It was rusty and in poor shape. That was a good idea. But he didn’t let everybody else know and didn’t have enough key copies for everyone. That was bad execution. Good ideas need good execution ideas too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Unexpected Lesson I Learned in a Hospital Exam Room</title>
   <link href="https://canro91.github.io/2025/08/24/ExaminationRoom/"/>
   <updated>2025-08-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/24/ExaminationRoom</id>
   <content type="html">&lt;p&gt;I’ve been in a hospital with a loved one more times than I wanted.&lt;/p&gt;

&lt;p&gt;Waiting in hospital rooms, I’ve written &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;10-idea lists&lt;/a&gt;, &lt;a href=&quot;/2025/04/20/MedAI/&quot;&gt;imagined the future of hospitals with AI&lt;/a&gt;, and &lt;a href=&quot;/2025/05/08/RandomLessons/&quot;&gt;learned some lessons about our bodies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before a recent exam, my loved one was so nervous that I had to enter the examination room too. The doctor could have dismissed her as just another nervous patient. But he calmed her down by explaining the possible diagnosis in simple words. That really worked.&lt;/p&gt;

&lt;p&gt;When my loved one apologized to him, his answer inspired me. “Don’t worry. This is my job and I do it because I really like it,” he said. You could tell by the way he spoke to his patients. Then he said, “I’m passionate about this. And here, between you and me, the hospital owes me money, but I still keep doing it.”&lt;/p&gt;

&lt;p&gt;Unlike my loved one’s doctor, I’m reluctant to use the word “passion.” That’s &lt;a href=&quot;/2025/03/19/ChangedMyMind/&quot;&gt;one of the subjects I’ve changed my mind about as a coder&lt;/a&gt;. The corporate world drained it out of me.&lt;/p&gt;

&lt;p&gt;But, whether the word passion or not, find something worth showing up for every day, even without pay. That’s what he taught me, even though I wasn’t the one he was treating.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TikTok Isn&apos;t a Life Coach Helping You Make Decisions</title>
   <link href="https://canro91.github.io/2025/08/23/ISawItOnTiktok/"/>
   <updated>2025-08-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/23/ISawItOnTiktok</id>
   <content type="html">&lt;p&gt;“I saw it on TikTok,” she told me.&lt;/p&gt;

&lt;p&gt;I was making small talk with a young girl at a hospital. She wanted to join the military. I asked her why, in a curious way. “It keeps appearing on my TikTok.”&lt;/p&gt;

&lt;p&gt;A couple of days ago, she watched one short of another young girl leading a female squad running around a military base. She kept seeing the same videos since then. “Of course, the more videos you watch…they will keep popping up,” I said.&lt;/p&gt;

&lt;p&gt;If you stop &lt;a href=&quot;/2025/07/29/InstagramLesson/&quot;&gt;your endless scrolling&lt;/a&gt; to watch a short, the algorithm will flood you with more. “Hey, she just watched the whole short, we’d be better off showing her similar content. She seems to like that type of content.” That’s what TikTok would think. Its job is to exploit our attention and keep us in the same echo chamber.&lt;/p&gt;

&lt;p&gt;Don’t make a decision just because TikTok keeps showing you the same videos. That’s not a sign from the Universe. That’s not guidance either. It’s manipulation.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Scams, scaling, and code reviewing</title>
   <link href="https://canro91.github.io/2025/08/22/FridayLinks/"/>
   <updated>2025-08-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/22/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. &lt;a href=&quot;https://blog.robbowley.net/2025/08/15/typing-is-not-the-bottleneck-illustrated/&quot;&gt;Typing isn’t the bottleneck&lt;/a&gt; (2min). With AI in the mix, it seems we’re optimizing the wrong variable.&lt;/p&gt;

&lt;p&gt;#2. Here’s &lt;a href=&quot;https://newsletter.eng-leadership.com/p/a-fake-tech-company-scam-heres-how&quot;&gt;someone who played the game of a scam job interview&lt;/a&gt; (20min) and lived to tell about it.&lt;/p&gt;

&lt;p&gt;#3. &lt;a href=&quot;https://derwiki.medium.com/do-things-that-dont-scale-and-then-don-t-scale-9fd2cd7e2156&quot;&gt;Not all side projects have to scale&lt;/a&gt; (3min) or become the next rising Silicon Valley startup. And that’s fine.&lt;/p&gt;

&lt;p&gt;#4. If you’re looking for alternatives to code reviews on GitHub, here’s &lt;a href=&quot;https://tigerbeetle.com/blog/2025-08-04-code-review-can-be-better/&quot;&gt;how code reviews can be better&lt;/a&gt; (6min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/08/18/TooLateToLearnCoding/&quot;&gt;why now is the best time to learn coding&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/08/20/RootFolder/&quot;&gt;how to navigate to the root folder of a git repo&lt;/a&gt; (1min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check my &lt;a href=&quot;https://imcsarag.gumroad.com/l/csharpfundamentalsbundle&quot;&gt;C# Fundamentals Bundle&lt;/a&gt;, two easy-to-follow video courses and a practical ebook—now at a 20% discount. With the essential tools every new coder needs to start writing clean and professional-grade C# code.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Six Takeaways from 21 Lessons for the 21st Century</title>
   <link href="https://canro91.github.io/2025/08/21/21Lessons/"/>
   <updated>2025-08-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/21/21Lessons</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;#1. A book can be built from posts and scattered ideas.&lt;/strong&gt; I’ve learned that while listening to &lt;a href=&quot;/2025/06/09/TrueWealth/&quot;&gt;Yuval Noah Harari discuss the book&lt;/a&gt; in a podcast interview. If he can do it, so can we.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. &lt;em&gt;“In a world deluged by irrelevant information, clarity is power.”&lt;/em&gt;&lt;/strong&gt; I loved &lt;a href=&quot;/2025/05/09/BleedInTheFirstLine/&quot;&gt;that opening line&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Homo Sapiens thrived as a species because we learned to collaborate.&lt;/strong&gt; And to collaborate, we created stories. For example, 1,000 years ago, a soccer World Cup would have been impossible. Not because we didn’t have planes, but because we lived in isolated, rival empires and tribes with little in common.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. The next device to hack is the brain.&lt;/strong&gt; Corporations already hack our minds using the data they collect about us. Now imagine the impact of biometric data: Instagram knowing when your pupils dilate and your heart rate changes when dumb scrolling. Most likely, algorithms will make the most important decisions for us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. We need to be more connected to our senses and emotions.&lt;/strong&gt; We no longer taste the seeds or fruits we once gathered to check for poisons. The old saying “Know Thyself” will be as relevant as it was 2,000 years ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. The most important skill to learn is meta-learning.&lt;/strong&gt; The ability to learn how to learn. We’ll have to reinvent ourselves multiple times to adapt. We can’t simply expect to be factory workers for life.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to Navigate to the Root Folder of a Git Repo</title>
   <link href="https://canro91.github.io/2025/08/20/RootFolder/"/>
   <updated>2025-08-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/20/RootFolder</id>
   <content type="html">&lt;p&gt;Recently I found myself &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt;ing to find the folder with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sln&lt;/code&gt; file. You know to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;Being a lazy coder, I thought of a better solution with a Bash script. But it turned out to be way easier with a Git command,&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;git rev-parse &lt;span class=&quot;nt&quot;&gt;--show-toplevel&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That would take you to the root folder of a git repo. And since my solution file is at the root, bingo! Kudos to &lt;a href=&quot;https://stackoverflow.com/a/56048850&quot;&gt;this SO answer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And the next lazy step was to &lt;a href=&quot;/2020/04/13/ProgramThatSave100Hours/&quot;&gt;create an alias&lt;/a&gt;,&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;groot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;cd &quot;$(git rev-parse --show-toplevel)&quot;&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why I Work from Home (It&apos;s Not the Pajamas)</title>
   <link href="https://canro91.github.io/2025/08/19/WhyWorkingFromHome/"/>
   <updated>2025-08-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/19/WhyWorkingFromHome</id>
   <content type="html">&lt;p&gt;Today life threw me a curve ball and forced me out of my home office.&lt;/p&gt;

&lt;p&gt;It was rush hour and I was still across town. Errands took me so long that I didn’t make it home before 5:00PM.&lt;/p&gt;

&lt;p&gt;I waited for 15 minutes next to ~100 people at a bus stop. After watching the time, I asked the lady in front of me, “Is it like this every day or just today?” “Yes! Every day! It’s rush hour,” she said.&lt;/p&gt;

&lt;p&gt;I got so desperate that I walked for 10 minutes to the closest bus terminal. Every stop had at least a dozen people waiting for the same bus. At the bus terminal, I waited for ~5 minutes for the right bus.&lt;/p&gt;

&lt;p&gt;Forty minutes later, I reached the closest station to home. It felt like a trip to the next city. Then another 5 minutes in line charging my card for tomorrow. And finally, another 10 minutes walking home.&lt;/p&gt;

&lt;p&gt;At home, I’d have been getting ready to work out or simply reading a book. And I still don’t get why people ask me if I like to &lt;a href=&quot;/2025/03/03/Boundaries/&quot;&gt;work from home&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Now Is the Best Time to Learn Programming (It&apos;s Not Too Late)</title>
   <link href="https://canro91.github.io/2025/08/18/TooLateToLearnCoding/"/>
   <updated>2025-08-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/18/TooLateToLearnCoding</id>
   <content type="html">&lt;p&gt;Today I found this question on &lt;a href=&quot;https://dev.to/eric_flores_eba19048116e5/im-trying-to-start-learning-webdev-but-i-dont-know-if-its-too-late-2713&quot;&gt;dev.to&lt;/a&gt; (with a tone of frustration in it):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I’m trying to start learning webdev but I don’t know if it’s too late.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Like investing, planting trees, and pretty much anything else in life, the best time to start was five years ago. But the next best time? Now.&lt;/p&gt;

&lt;p&gt;Decades ago, people learned from reference manuals and magazines. Paid for compilers. And suffered with slow and expensive internet connections. You could only access a computer in universities. &lt;a href=&quot;/2025/04/15/FirstTimeISawAComputer/&quot;&gt;Having one at home was a complete luxury&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fast forward to today, your phone has more power than the computer that put a man on the Moon.&lt;/p&gt;

&lt;p&gt;Now, anyone can start learning to code by asking ChatGPT for a 3-month learning guide and following YouTube tutorials. All for free.&lt;/p&gt;

&lt;p&gt;If you’re asking the same question, don’t be discouraged by AI.&lt;/p&gt;

&lt;p&gt;Blame all &lt;a href=&quot;/2024/12/08/CEOVsJanitor/&quot;&gt;the misleading headlines&lt;/a&gt;. &lt;em&gt;“More than X% of new code at $BigCorp is generated by AI.”&lt;/em&gt; More often than not, they’re a marketing strategy from AI companies.&lt;/p&gt;

&lt;p&gt;AI is making coding faster and cheaper. Sure.&lt;/p&gt;

&lt;p&gt;But most of the real work still happens through collaborating in meetings and discussions, not at the keyboard. &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;AI isn’t replacing coders&lt;/a&gt; (for real) anytime soon because coding is about thinking and problem-solving. Typing is just the surface.&lt;/p&gt;

&lt;p&gt;And that’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;, a practical guide to the skills that make you a confident, thoughtful coder. It’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt; and future-proof your skills.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Re: Why LinkedIn Rewards Mediocrity</title>
   <link href="https://canro91.github.io/2025/08/17/LinkedInMediocrity/"/>
   <updated>2025-08-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/17/LinkedInMediocrity</id>
   <content type="html">&lt;p&gt;Elliot Smith nailed it in his post &lt;a href=&quot;https://www.elliotcsmith.com/linkedin-toxic-mediocrity/&quot;&gt;Why LinkedIn Rewards Mediocrity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s hard to disagree with that headline and his main idea. Yes, &lt;a href=&quot;/2025/07/27/LinkedInWeirdness/&quot;&gt;LinkedIn is a weird place&lt;/a&gt;. We do crazy things for a moment of fame or to impress future employers.&lt;/p&gt;

&lt;p&gt;I’ve been writing consistently on LinkedIn since 2024, and here are my reactions:&lt;/p&gt;

&lt;h2 id=&quot;its-built-for-virality&quot;&gt;It’s built for virality&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Honestly, the best approach is to remember that LinkedIn is a website owned by Microsoft, trying to make money for Microsoft, based on time spent on the site.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Absolutely! Like any other social platform, they want us trapped.&lt;/p&gt;

&lt;p&gt;If you have a LinkedIn account, you’ve already received the useless “someone on LinkedIn viewed your profile” email and plenty of others. The more time we spend there, the more ads they show and the more money they make. Our attention is the product they sell.&lt;/p&gt;

&lt;p&gt;The feed, the algorithm, and the platform itself are created for virality, not depth. And truth be told, nobody goes to social media for depth. When was the last time you went there for that? We go there to scroll until it’s time to clock out. &lt;a href=&quot;/2025/07/29/InstagramLesson/&quot;&gt;We might learn something dumb scrolling sometimes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Social media might offer ideas, but only in 280 characters or a flashy image. Funny enough, my most viewed posts have been listicles and short controversial posts. &lt;a href=&quot;/2025/06/23/Listicles/&quot;&gt;They crush it on social media&lt;/a&gt;. But for depth, we have newsletters and books.&lt;/p&gt;

&lt;h2 id=&quot;it-has-good-content&quot;&gt;It has good content&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Lots of people who write good content don’t live on LinkedIn, they might repurpose things for the platform but they exist elsewhere.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s true.&lt;/p&gt;

&lt;p&gt;I use social media to test ideas and promote &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;my coding newsletter&lt;/a&gt; and blog. No shame in being “salesy.” That’s &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;what most creators do&lt;/a&gt;. After all, social media is built for dopamine hits, not depth.&lt;/p&gt;

&lt;h2 id=&quot;its-easy-to-support-good-content&quot;&gt;It’s easy to support good content&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;If you’re more of a consumer than a producer and you want to help make things better the best thing you can do is reward the real stuff. Find those people who aren’t playing the game and promote that instead.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;100% agree. If you comment that something is clickbait, you’ll only boost it. More comments = larger audience. Simply ignore that type of content. Engage with content worth spreading. That’s how we can fix the feed.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We&apos;ve Lost the Joy of Seeing Tech for the First Time</title>
   <link href="https://canro91.github.io/2025/08/16/Magic/"/>
   <updated>2025-08-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/16/Magic</id>
   <content type="html">&lt;p&gt;The other day, I shared my story of &lt;a href=&quot;/2025/04/15/FirstTimeISawAComputer/&quot;&gt;how I met my first computer&lt;/a&gt; on dev.to. It resonated with at least a dozen people. From the 1 KB RAM computer story to the grassy hill wallpaper of Windows XP.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/canro91/the-first-time-i-saw-a-computer-a-bit-of-nostalgia-4a8a/comments&quot;&gt;All comments&lt;/a&gt; had something in common: Seeing a computer for the first time was like a magical moment. For many, changing letters on a screen felt magical 10 or 20 years ago.&lt;/p&gt;

&lt;p&gt;Most of us ended up choosing programming because of the mystery and magic of seeing computers for the first time.&lt;/p&gt;

&lt;p&gt;Maybe my first encounter with a computer wasn’t that magical. I didn’t know then I’d work with with computers. But I narrowed down all my options until a computer-related one was left. I owe it to my STEM classes and having a computer at home, a luxury for universities and big firms back then.&lt;/p&gt;

&lt;p&gt;These days, computers are everyday tools we barely notice. That magic has faded away.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Live coding, spellcheckers, and AI rejection</title>
   <link href="https://canro91.github.io/2025/08/15/FridayLinks/"/>
   <updated>2025-08-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/15/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. One senior developer failed at a live coding interview session. Funny enough, once the interview was over, he solved the exercise. Arrggg! He wrote a breakdown of &lt;a href=&quot;https://hadid.dev/posts/living-coding/&quot;&gt;why live coding sucks&lt;/a&gt; (9min).&lt;/p&gt;

&lt;p&gt;#2. Here are &lt;a href=&quot;https://benjamincongdon.me/blog/2025/08/11/Fifty-Bits-of-Career-Advice/&quot;&gt;50 bits of career advice&lt;/a&gt; (12min). Bit#26 is what I call “my code is not my baby.”&lt;/p&gt;

&lt;p&gt;#3. It’s interesting to notice how coding has changed over the years. &lt;a href=&quot;https://prog21.dadgum.com/29.html&quot;&gt;Coding a spellchecker used to be a huge challenge&lt;/a&gt; (3min). These days we have plenty of memory, we don’t really care anymore.&lt;/p&gt;

&lt;p&gt;#4. This is a coder vs huge corporation story. He wrote a piece of code that a major AI corporation uses. But they rejected him when he applied to work there. He &lt;a href=&quot;https://grell.dev/blog/ai_rejection&quot;&gt;gave AI arms and legs, then it rejected him&lt;/a&gt; (7min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/08/13/OneCodingLesson/&quot;&gt;the one lesson I wish I’d known when I started coding&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/08/09/ExtensionMethods/&quot;&gt;some C# extension methods I stole from Reddit&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;Coming soon: the “C# Fundamentals Bundle,” all of my beginner-friendly C# video courses to help you master the language from the ground up. Launching in just a few weeks, so stay tuned!&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Forget Perfect Influencers&apos; Routines. Embrace Imperfect Days Too</title>
   <link href="https://canro91.github.io/2025/08/14/ImperfectDays/"/>
   <updated>2025-08-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/14/ImperfectDays</id>
   <content type="html">&lt;p&gt;Today, Derek Siddoway’s &lt;a href=&quot;https://www.linkedin.com/posts/dereksiddoway_heres-a-list-of-everything-i-did-and-didnt-activity-7361613401061560322-2d82&quot;&gt;LinkedIn post&lt;/a&gt; deeply resonated.&lt;/p&gt;

&lt;p&gt;He wrote that he didn’t work out or sleep early. He shared a honest “didn’t do” list with unhealthy entries: burger for lunch and two cans of Mountain Drew. But he managed to find time to write.&lt;/p&gt;

&lt;p&gt;I’ve had a busy week too. I’ve missed &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;my daily practice&lt;/a&gt;. I haven’t worked out. And I’ve barely kept &lt;a href=&quot;/2025/05/20/200DailyPosts/&quot;&gt;my daily streak intact&lt;/a&gt;. But like Derek, I found time for one thing that made my days matter: I supported my family when they needed me… and hit Publish.&lt;/p&gt;

&lt;p&gt;Imperfect days happen. Ride the tide. One action could turn an imperfect day into a good one. &lt;a href=&quot;/2025/08/11/Present/&quot;&gt;Tomorrow is always new&lt;/a&gt;. That’s what I’m learning to do.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The One Lesson I Wish I&apos;d Known When I Started Coding</title>
   <link href="https://canro91.github.io/2025/08/13/OneCodingLesson/"/>
   <updated>2025-08-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/13/OneCodingLesson</id>
   <content type="html">&lt;p&gt;A Redditor recently asked for tips to become a better programmer &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1mnvsj9/what_do_you_wish_you_knew_when_you_started_coding/&quot;&gt;here&lt;/a&gt;. The kind of tips we wish we had known when we started coding.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I’ve been taking a few courses here and there for c# as a side language I’m learning. Curious if you know something I don’t and have tips for making other newcomers a better programmer… Lmk what you wish you could have learned earlier thst would of helped you progress faster!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I already wrote about &lt;a href=&quot;/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer/&quot;&gt;four career lessons I wish I had known here&lt;/a&gt;. But there’s a coding lesson before those four.&lt;/p&gt;

&lt;p&gt;You’re not going to like it, but:&lt;/p&gt;

&lt;h2 id=&quot;dont-obsess-over-syntax-and-programming-languages&quot;&gt;Don’t obsess over syntax and programming languages.&lt;/h2&gt;

&lt;p&gt;Coding isn’t about learning every feature of a language.&lt;/p&gt;

&lt;p&gt;You don’t need a huge list of tools to start. With HTML/CSS/JavaScript, one backend language, and a good amount of SQL, you have enough to make your way through the coding world.&lt;/p&gt;

&lt;p&gt;You could learn the rest by doing and Googling.&lt;/p&gt;

&lt;h2 id=&quot;more-important-than-syntax-and-languages-is-product-thinking&quot;&gt;More important than syntax and languages is product thinking.&lt;/h2&gt;

&lt;p&gt;Instead of obsessing with the best language features, think in terms of the product you’re building.&lt;/p&gt;

&lt;p&gt;Ask the questions most coders wouldn’t dare (or care) to ask:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Are we building what users really need?&lt;/li&gt;
  &lt;li&gt;How will they use our product?&lt;/li&gt;
  &lt;li&gt;How many users will we have?&lt;/li&gt;
  &lt;li&gt;How much are we charging?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ask about marketing, sales, or anything beyond coding. Get interested in the business behind the code you’re writing.&lt;/p&gt;

&lt;p&gt;That attitude will make you &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;stand out in any team&lt;/a&gt;. It will save you from building the wrong features or &lt;a href=&quot;/2025/05/17/NeedForScale/&quot;&gt;optimizing for a scale you won’t have&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Product thinking will open doors to climb the corporate ladder faster.&lt;/p&gt;

&lt;h2 id=&quot;theres-more-to-coding-than-typing-symbols-on-text-files&quot;&gt;There’s more to coding than typing symbols on text files.&lt;/h2&gt;

&lt;p&gt;After 10+ years, I’ve learned that &lt;a href=&quot;/2025/06/14/Senior/&quot;&gt;the more senior you become&lt;/a&gt;, the less it’s about syntax and the more it’s about how you collaborate, &lt;a href=&quot;/2025/07/14/CommAtWork/&quot;&gt;communicate&lt;/a&gt;, and solve business problems.&lt;/p&gt;

&lt;p&gt;I wish someone had told me that earlier. As a junior coder, I obsessed over learning languages and ignored other valuable skills: product thinking, teamwork, and clear communication.&lt;/p&gt;

&lt;p&gt;And that’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding,&lt;/em&gt; the guide to the lessons I wish I’d known from day one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Starbucks in South Korea Has a Starving Crowd...and They’re Not Serving Them</title>
   <link href="https://canro91.github.io/2025/08/12/StarvingCrowd/"/>
   <updated>2025-08-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/12/StarvingCrowd</id>
   <content type="html">&lt;p&gt;According to &lt;a href=&quot;https://fortune.com/2025/08/11/starbucks-south-korea-policy-desktop-computer-printer-ban-cagongjok/&quot;&gt;fortune.com&lt;/a&gt;, Starbucks in South Korea has forbidden their clients to bring desktop computers, printers, and “bulky items.”&lt;/p&gt;

&lt;p&gt;After the COVID crisis, Starbucks isn’t just a place for coffee in South Korea, but for cheap coworking spaces.&lt;/p&gt;

&lt;p&gt;It reminded me of a lesson from The Boron Letters. That’s a series of letters (turned into a book) he legendary copywriter Gary Halbert wrote to his son from prison. He taught that, to sell more hamburgers, you don’t need the best recipe or location, but a starving crowd.&lt;/p&gt;

&lt;p&gt;Starbucks has a starving crowd. Their starving crowd is asking them not only for coffee but for something else. It’s time to pivot, not to forbid that starving crowd to be hungry.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Mindset Shift I Adopted After Recovering From Burnout</title>
   <link href="https://canro91.github.io/2025/08/11/Present/"/>
   <updated>2025-08-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/11/Present</id>
   <content type="html">&lt;p&gt;By the end of 2023, I had burned out.&lt;/p&gt;

&lt;p&gt;I had tied my sense of purpose to my job while forgetting my health and hobbies. That took a toll on me.&lt;/p&gt;

&lt;p&gt;To recover, I started with an information diet. No more mindless scrolling, Hacker News, or even music. Just talks from creators who have been through it. &lt;a href=&quot;https://borjavilaseca.com/&quot;&gt;Borja Vilaseca&lt;/a&gt; was one of them.&lt;/p&gt;

&lt;p&gt;Another inspiring figure was &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;. I read about his bankruptcies and how he bounced back. I started to follow one of his habits: doing something for my mind, body, and spirit. He calls it: &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;The daily practice&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since recovering, I’ve embraced a simple mantra: one day at a time.&lt;/p&gt;

&lt;p&gt;No matter how tough the day is, trust God (or the Universe or Life) and rest. Tomorrow is a fresh start.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/12/19/TimeTravel/&quot;&gt;Stop time traveling&lt;/a&gt;. Because living in the past makes you resentful, and worrying about the future makes you anxious. Stay present.&lt;/p&gt;

&lt;p&gt;Working on my body, mind, and spirit was key to recovering from burnout. That’s why I made it one of the ideas in &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=mindset-shift-i-adopted-recovering-burnout-code&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. A book about the tiny daily actions that actually change your life.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Worst Financial Tip I Ever Got—and What to Do Instead</title>
   <link href="https://canro91.github.io/2025/08/10/WorstFinancialTip/"/>
   <updated>2025-08-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/10/WorstFinancialTip</id>
   <content type="html">&lt;p&gt;“Use your credit card only for emergencies.”&lt;/p&gt;

&lt;p&gt;Sure, there are good intentions behind that tip. Credit cards have some of the highest interest rates on the market. And, like any debt, they’re a loaded gun. Treat them carefully or they’ll hurt you.&lt;/p&gt;

&lt;p&gt;But for emergencies, taking on debt with a high interest rate isn’t a good idea. So how do you prepare for emergencies? With a financial cushion.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Richest Man in Babylon&lt;/em&gt; taught me about a financial cushion. It’s &lt;a href=&quot;/2025/01/05/MoneyBooks/&quot;&gt;one of the best money books I’ve read&lt;/a&gt;. “From every 10 coins you earn, keep 1 and spend the other 9.” That’s how I remember the lesson. That simple habit helped me &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;survive a layoff&lt;/a&gt; and even afford a mini-retirement.&lt;/p&gt;

&lt;p&gt;Use your credit cards like cash. If you can’t afford it, don’t swipe. Cards are a payment tool, not a source of money you don’t have.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Stealing Some of Reddit&apos;s C# Extension Methods</title>
   <link href="https://canro91.github.io/2025/08/09/ExtensionMethods/"/>
   <updated>2025-08-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/09/ExtensionMethods</id>
   <content type="html">&lt;p&gt;Today, I found &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1mkrlcc/what_is_the_lowest_effort_highest_impact_helper/&quot;&gt;this Reddit question&lt;/a&gt; asking for the lower effort extension methods we’ve written.&lt;/p&gt;

&lt;p&gt;And like &lt;a href=&quot;/2025/02/19/YouAreNotAProgrammerUntil/&quot;&gt;any real C# programmer&lt;/a&gt;, I have my own &lt;a href=&quot;/2022/12/16/HelperMethodsOnCollections/&quot;&gt;set of extension methods to work with collections&lt;/a&gt;. But I ended up stealing one method from that thread: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Choose()&lt;/code&gt;. It applies a transformation to a list and only returns the resulting values different from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It turns out, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Choose()&lt;/code&gt; comes from F#’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&lt;/code&gt; type. So my next step was to sneak into F#’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&lt;/code&gt; type and steal some of its methods.&lt;/p&gt;

&lt;p&gt;Here they are,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;projection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TryPick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Replicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s a quick example of how to use them,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The Matrix&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Gladiator&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2008&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The Dark Knight&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Freaky Friday&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;years&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;years&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;Freaky Friday&quot;, &quot;The Matrix&quot;, &quot;Gladiator&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;years&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;Freaky Friday&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: PHP turning 30, reading code, and passion</title>
   <link href="https://canro91.github.io/2025/08/08/FridayLinks/"/>
   <updated>2025-08-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/08/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. PHP was my first love. I spent countless hours coding a recipe catalog using CodeIgniter to learn coding. To my surprise PHP just turned 30. And after 30 years, PHP has become &lt;a href=&quot;https://deprogrammaticaipsum.com/the-toyota-corolla-of-programming/&quot;&gt;the Toyota Coralla of programming&lt;/a&gt; (9min).&lt;/p&gt;

&lt;p&gt;#2. It’s easy to spot AI-generated posts. Just look at the opening sentence. Look for anything like “In today’s fast-paced world.” But what about code? Here’s &lt;a href=&quot;https://alexkondov.com/i-know-when-youre-vibe-coding/&quot;&gt;how to know when someone is vibe coding&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;#3. Reading code is an underrated coding skill. These days of too much vibe coding, reading code is the forgotten skill. If vibe coders only &lt;a href=&quot;https://etsd.tech/posts/rtfc/&quot;&gt;read the f*ing code&lt;/a&gt; (9min).&lt;/p&gt;

&lt;p&gt;#4. What’s the secret behind success? It seems &lt;a href=&quot;https://birming.com/2025/07/27/passion-as-the-recipe-for/&quot;&gt;passion has a lot to do with it&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, this past week I wrote on my blog about &lt;a href=&quot;/2025/08/06/Prompting/&quot;&gt;how programming is becoming prompting&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/08/04/BabyShower/&quot;&gt;7 life events that deserver a “shower” too&lt;/a&gt; (2min). That last one was half-joking. But the business shower doesn’t sound that crazy.&lt;/p&gt;

&lt;p&gt;Last week I found out a Brazilian YouTuber translated one of my posts and reacted to it on his channel. &lt;a href=&quot;/2025/08/02/StandOut/&quot;&gt;I share the video here&lt;/a&gt;. Funny things that happen.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;Coming soon: the “C# Fundamentals Bundle,” all of my beginner-friendly C# video courses to help you master the language from the ground up. Launching in just a few weeks, so stay tuned!&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>5 Things Every New Coder Should Watch, Ask, Read, Do, and Pass On</title>
   <link href="https://canro91.github.io/2025/08/07/WorthyFive/"/>
   <updated>2025-08-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/07/WorthyFive</id>
   <content type="html">&lt;p&gt;While curating this week’s &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;Friday Links&lt;/a&gt; email, I found out about Dense Discovery and got inspired.&lt;/p&gt;

&lt;p&gt;That newsletter features a section called Worthy Five where a subscriber shares 5 things worth watching, reading, asking…&lt;/p&gt;

&lt;p&gt;So I’m stealing (like an artist) that idea, and tweaking it for new coders:&lt;/p&gt;

&lt;h2 id=&quot;1-a-video-worth-watching&quot;&gt;#1. A video worth watching.&lt;/h2&gt;

&lt;p&gt;Well, it’s more a lecture series. Watch Stanford CS106A &lt;a href=&quot;https://www.youtube.com/watch?v=KkMDCCdjyW8&amp;amp;list=PL84A56BC7F4A1F852&quot;&gt;on YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s an introductory course to programming. I watched all the lectures in the 2010s while learning to code. I loved the energy of the teacher and the exercises using video games to teach programming concepts.&lt;/p&gt;

&lt;p&gt;Watch the first few lectures, you’ll love it.&lt;/p&gt;

&lt;h2 id=&quot;2-a-question-worth-asking&quot;&gt;#2. A question worth asking.&lt;/h2&gt;

&lt;p&gt;Ask what you want out of your career.&lt;/p&gt;

&lt;p&gt;I jumped from job to job without any plan until I got bored or fired. At the end, &lt;a href=&quot;/2025/03/22/IsBurnoutInevitable/&quot;&gt;I burned out&lt;/a&gt; after trying to make a “good” job work for me. I never took the time to set an intention for my career.&lt;/p&gt;

&lt;p&gt;Come up with a career plan or goal. Remember you can always change it.&lt;/p&gt;

&lt;h2 id=&quot;3-a-book-worth-reading&quot;&gt;#3. A book worth reading.&lt;/h2&gt;

&lt;p&gt;OK, let me give you two.&lt;/p&gt;

&lt;p&gt;First, &lt;a href=&quot;/2020/06/15/CleanCoder/&quot;&gt;Clean Coder&lt;/a&gt;. I enjoyed this one more than Clean Code. Clean Coder isn’t about writing code, but about being a professional developer. It covers professionalism, unit testing, and estimates.&lt;/p&gt;

&lt;p&gt;The other one? Code That Fits Into Your Head. This isn’t precisely a book on syntax, but rather one about programming practices. Its main point? Write code in such a way you can keep its details in your head.&lt;/p&gt;

&lt;p&gt;If you don’t know which one to pick first, go with Clean Coder.&lt;/p&gt;

&lt;h2 id=&quot;4-an-activity-worth-doing&quot;&gt;#4. An activity worth doing&lt;/h2&gt;

&lt;p&gt;Write!&lt;/p&gt;

&lt;p&gt;I’m biased here. I love writing. But, seriously, write anywhere online. To &lt;a href=&quot;/2024/11/08/WhyWriting/&quot;&gt;put your thoughts on paper&lt;/a&gt;. To document your learning.&lt;/p&gt;

&lt;p&gt;Writing opens doors you can’t even imagine. For example, &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;my blog has done more than a portfolio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you don’t know how to start, &lt;a href=&quot;/2025/04/10/TilPosts/&quot;&gt;write TIL posts&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;5-a-piece-of-advice-worth-passing-on&quot;&gt;#5. A piece of advice worth passing on&lt;/h2&gt;

&lt;p&gt;Back at my first job, a coworker gave me this piece of advice:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Imagine you only make half of your salary, save and invest the other half.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Probably the best piece of advice I’ve received for free. Here are &lt;a href=&quot;/2025/06/29/BestAdvice/&quot;&gt;another two I never asked&lt;/a&gt;. That one helped me survive a layoff. Definitely worth passing on.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Re: Programming Is Becoming Prompting</title>
   <link href="https://canro91.github.io/2025/08/06/Prompting/"/>
   <updated>2025-08-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/06/Prompting</id>
   <content type="html">&lt;p&gt;Leon Martin wrote on &lt;a href=&quot;https://dev.to/holasoymalva/programming-is-becoming-prompting-2odn&quot;&gt;dev.to&lt;/a&gt; that programming is becoming prompting.&lt;/p&gt;

&lt;p&gt;And that’s true. The barrier to entry for coding is getting lower and lower.&lt;/p&gt;

&lt;p&gt;A few years ago, it was the “Learn to Code” movement. Even Barack Obama replied to a coding question in an interview. One about sorting algorithms.&lt;/p&gt;

&lt;p&gt;These days, with AI, anyone is one paragraph away from creating something that works.&lt;/p&gt;

&lt;p&gt;Leon made two interesting points in his post:&lt;/p&gt;

&lt;h2 id=&quot;1-too-much-ai-is-bad-for-your-health&quot;&gt;#1. Too much AI is bad for your health&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“…when you start writing prompts instead of functions, you stop flexing those problem-solving muscles that got you into programming in the first place.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Been there! I’ve been using Copilot during my coding sessions. And the other day, I felt the urge to use AI instead of working through a simple problem.&lt;/p&gt;

&lt;p&gt;And &lt;a href=&quot;/2025/07/13/TheProblemWithAI/&quot;&gt;that’s the real danger of too much AI&lt;/a&gt;. Like the coder who asked on Reddit &lt;a href=&quot;/2025/06/28/AIRecovery/&quot;&gt;how to get his coding skills back&lt;/a&gt; after vibing for too long.&lt;/p&gt;

&lt;h2 id=&quot;2-ai-needs-the-right-hands&quot;&gt;#2. AI needs the right hands&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“&lt;em&gt;Knowing how to code is still the superpower. The prompt is just a shortcut.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Absolutely! &lt;a href=&quot;/2025/03/31/AIAndCalculators/&quot;&gt;AI is just like calculators in math class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You could have the most powerful calculator in your hands during an exam, but if you don’t know what you want to compute, it’s useless. You will fail that exam.&lt;/p&gt;

&lt;p&gt;AI is like an Iron Man suit. But it still needs Tony Stark inside it to save the day.&lt;/p&gt;

&lt;p&gt;Build the coding skills, then use AI to amplify them. Work on your debugging, problem-solving, and clear communication skills.&lt;/p&gt;

&lt;p&gt;If you want a guide to sharpen your coding skills, I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; Some lessons are conventional. Some were learned the hard way. And a few are weird. But all battle-tested.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=programming-is-becoming-prompting&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt; It’s the roadmap I wish I had when I was starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My Simple LinkedIn Strategy for Growth (Ignoring All Gurus&apos; Advice)</title>
   <link href="https://canro91.github.io/2025/08/05/LinkedInStrategy/"/>
   <updated>2025-08-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/05/LinkedInStrategy</id>
   <content type="html">&lt;p&gt;Since January 2024, I’ve been growing my LinkedIn account.&lt;/p&gt;

&lt;p&gt;As soon as you join the creator crowd on LinkedIn, you step into the gurus’ land with all kinds of advice:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Comment more: Comment for 1 hour a day.&lt;/li&gt;
  &lt;li&gt;Don’t schedule your posts: Always hit “Post.”&lt;/li&gt;
  &lt;li&gt;Engage in the first hour to “warm up” the algorithm.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow the gurus’ advice, revealed after exclusively deciphering the algorithm, and you’d end up offering comments to the LinkedIn gods, writing your post of the day, and then commenting for another 1 or 2 hours under top creators’ posts to beg for their attention.&lt;/p&gt;

&lt;p&gt;After &lt;a href=&quot;/2025/06/15/LinkedIn/&quot;&gt;writing over 300 posts&lt;/a&gt;, I’ve decided to ignore all those pieces of advice.&lt;/p&gt;

&lt;p&gt;Instead, I batch and schedule my posts, and only engage to support fellow creators. And, I also make sure to reply to any comment on my posts.&lt;/p&gt;

&lt;p&gt;Because you don’t have to play by anyone’s rules or follow anyone’s revealed secrets. You have to come up with &lt;a href=&quot;/2024/12/14/Consistency/&quot;&gt;an easy and sustainable strategy&lt;/a&gt; to show up consistently. And that’s true not just for LinkedIn, but for any creative pursuit.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Life Events That Deserve a Shower Too—Not Just Babies!</title>
   <link href="https://canro91.github.io/2025/08/04/BabyShower/"/>
   <updated>2025-08-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/04/BabyShower</id>
   <content type="html">&lt;p&gt;When you’re a teenager, you get invitations to “sweet 16” or quinceañera parties.&lt;/p&gt;

&lt;p&gt;When you’re in your 20s, you get wedding invitations. In your 30s, baby shower invitations.&lt;/p&gt;

&lt;p&gt;This week, my best friend invited me to her first baby shower.&lt;/p&gt;

&lt;p&gt;That made me think of other types of “shower” celebrations we as adults could host:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. A business shower:&lt;/strong&gt; Guests support your new business by buying your product, leaving a 5-star review, and maybe even donating to cover some business expenses. Maybe a little something to cover your accountant’s fee. Taxes don’t file themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. A breakup shower:&lt;/strong&gt; Guests show up with breakup survival kits: ice cream, wine, a sad playlist, and romantic movies. Or whatever people eat and drink to get over a breakup. Maybe a guest can introduce you to someone or arrange a blind date for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. A new apartment shower:&lt;/strong&gt; If you leave your parents’ nest or just get married, you can host a new apartment shower. Guests bring utensils and cleaning supplies to fill your empty new apartment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. A divorce shower:&lt;/strong&gt; This shower is for turning “that page.” Your guests help you throw away stuff from your past life or simply buy anything that reminds you of your ex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. A college shower:&lt;/strong&gt; About to start college? Invite your family and friends to help you with… Wondering what college students need these days? Probably notebooks, a laptop and a ChatGPT subscription. And maybe ask for some extra money to pay for the college tuition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. A first job shower:&lt;/strong&gt; &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;Got your first job&lt;/a&gt;? Congrats! Host a first job shower. Guests give you a jacket and tie, help you with a professional headshot to update your LinkedIn account, and buy you a Netflix subscription so you can unplug after a busy day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. A layoff shower:&lt;/strong&gt; &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;Got laid off&lt;/a&gt;? Sorry to hear that. You’re not alone. Host a layoff shower: guests help &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;revamp your CV&lt;/a&gt;, write LinkedIn recommendations, or maybe offer a referral. Now that I think about it, this would be like a breakup shower. These days I think pretty much everybody in tech would run this one. Wait if I got laid off more than one year ago, can I still host one? Asking for my LinkedIn account.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Fast Content Experiments I&apos;m Running This Month (to Make a Buck or Two)</title>
   <link href="https://canro91.github.io/2025/08/03/ContentExperiments/"/>
   <updated>2025-08-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/03/ContentExperiments</id>
   <content type="html">&lt;p&gt;Recently, I learned about the 10,000 experiment rule.&lt;/p&gt;

&lt;p&gt;I found it on &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Skip The Line&lt;/a&gt; by &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;. That is, instead of aiming for 10,000 hours to mastery, run quick, cheap, and easy-to-do experiments.&lt;/p&gt;

&lt;p&gt;Yesterday, as part of my daily routine of writing 10 bad ideas a day, I thought about experiments I could run this month. So here are 7 of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Change my LinkedIn bio to include “Ghostwriter.”&lt;/strong&gt; Often the only thing we need to start with a new offer or service is changing our headline and email signature. I’m changing my LinkedIn headline and bio to start my LinkedIn ghostwriting gig.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Upload my coding courses to my Gumroad store.&lt;/strong&gt; I offer three C# coding courses on Udemy. The thing is, Udemy keeps students contacts and takes a big cut.&lt;/p&gt;

&lt;p&gt;Instead of growing Udemy’s business, I’m hosting my coding courses on &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; too. I control the course prices and have direct access to my students.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Change all prices on my Gumroad store to end on 99.&lt;/strong&gt; There’s a lot of psychology research behind pricing. Long story short, products with prices that end in 99 sell more. Let’s see if that’s true.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Instead of giving away some ebooks, charge $1.&lt;/strong&gt; Or $0.99 to follow #3. I’ve read that $0.99 is the new free for books, given that “free” readers rarely convert to paying ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Reformat my free ebooks and point to my paid ones.&lt;/strong&gt; I’ll add a “From the same author” section to each book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Increase the suggested prices of all my free products.&lt;/strong&gt; I offer free books on Gumroad, with pay-what-you-want pricing. I only set a suggested price. I’m increasing that price from $5 to $7. Maybe I can subconsciously influence my visitors to leave a higher tip. Muahaha!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Offer all my C# coding courses on a “C# Fundamentals Bundle.”&lt;/strong&gt; After following #2 and #3, I’m bundling my C# courses together and offering it for $12.99 or something. If I sell one or two bundles, that’s a win!&lt;/p&gt;

&lt;p&gt;Let’s see which ones take off. Either way, I’ll learn something… and keep my daily streak alive.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Portuguese-Speaking Coders: Want to Stand Out at Work?</title>
   <link href="https://canro91.github.io/2025/08/02/StandOut/"/>
   <updated>2025-08-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/02/StandOut</id>
   <content type="html">&lt;p&gt;Oi, galera! You don’t need to overwork to stand out in your team.&lt;/p&gt;

&lt;p&gt;Paulo Cardoso reacted to my post with &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;10 tips to stand out at work&lt;/a&gt;, sharing his thoughts on his YouTube channel. Well, actually, he reacted to the Medium version.&lt;/p&gt;

&lt;p&gt;He translated and expanded on all 10 points. He added more context to each point. All in Portuguese.&lt;/p&gt;

&lt;p&gt;Check out his full breakdown below.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/yu4PHP84lwM?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Being a good programmer, meetings, and AI</title>
   <link href="https://canro91.github.io/2025/08/01/FridayLinks/"/>
   <updated>2025-08-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/08/01/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. I had ~5 meetings to decide what to do on each sprint. And I wasn’t doing any rocket science, just boring enterprise software. Meetings are the #1 distraction and a necessary evil. Here’s &lt;a href=&quot;https://abitmighty.com/posts/the-ultimate-meeting-culture&quot;&gt;how to create the ultimate meeting culture&lt;/a&gt; (8min).&lt;/p&gt;

&lt;p&gt;#2. If you’re a new manager or you want to become one, here’s &lt;a href=&quot;https://scottkosman.com/post/blog/so-youre-a-manager-now/&quot;&gt;a down-to-earth guide for new managers&lt;/a&gt; (7min). I really liked the coach analogy.&lt;/p&gt;

&lt;p&gt;#3. Here’s another way &lt;a href=&quot;https://www.gizvault.com/archives/the-lost-path-to-seniorhood&quot;&gt;how AI is “killing” junior coders&lt;/a&gt; (4min) in the open source world.&lt;/p&gt;

&lt;p&gt;#4. If you’ve ever wondered if you’re a good programmer, &lt;a href=&quot;https://thecodist.com/what-is-a-good-programmer/&quot;&gt;read this post/reflection&lt;/a&gt;. It helps with the impostor syndrome.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/07/30/BuyingAMattress/&quot;&gt;the business lesson I learned buying a new mattress&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/07/31/CareerRegrets/&quot;&gt;10 career actions you will never regret&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>10 Actions You Will Never Regret Doing for Your Career</title>
   <link href="https://canro91.github.io/2025/07/31/CareerRegrets/"/>
   <updated>2025-07-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/31/CareerRegrets</id>
   <content type="html">&lt;p&gt;My corporate journey didn’t come with an instruction manual.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;I landed my first job&lt;/a&gt; because that’s what everyone does and what I was supposed to do. I joined the corporate world without any goal, other than “gain experience.” Which I did it.&lt;/p&gt;

&lt;p&gt;But &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;I was fired from my first job&lt;/a&gt;, got bored from my second, and &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;got laid off&lt;/a&gt; from my last one. No survival kit or instruction manual. Only trial and error.&lt;/p&gt;

&lt;p&gt;If I could start all over again, here are 10 actions I will never regret doing:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Change jobs often&lt;/li&gt;
  &lt;li&gt;Learn new skills constantly&lt;/li&gt;
  &lt;li&gt;Set a career goal or intention&lt;/li&gt;
  &lt;li&gt;Practice hobbies outside work&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/06/10/StartingFromZero/&quot;&gt;Build a strong online presence earlier&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Invest in coaching, mentoring, and education&lt;/li&gt;
  &lt;li&gt;Learn a second or third language&lt;/li&gt;
  &lt;li&gt;Build multiple income sources&lt;/li&gt;
  &lt;li&gt;Grow a professional network&lt;/li&gt;
  &lt;li&gt;Have an emergency fund&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And more importantly, figure out my own career and plan life instead of accepting the default path. &lt;a href=&quot;/2025/05/07/LifeLesson/&quot;&gt;That’s a lesson that took me 10 years to learn&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Business Lesson I Learned From Buying a Mattress</title>
   <link href="https://canro91.github.io/2025/07/30/BuyingAMattress/"/>
   <updated>2025-07-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/30/BuyingAMattress</id>
   <content type="html">&lt;p&gt;&lt;em&gt;“Sorry, it’s too late. There’s nothing to do,”&lt;/em&gt; a salesgirl told me.&lt;/p&gt;

&lt;h2 id=&quot;the-mattress&quot;&gt;The mattress&lt;/h2&gt;

&lt;p&gt;Earlier that day, the mattress I had ordered was delivered. I was told I should get it assembled and ready to use. But the delivery man told me, &lt;em&gt;“I was hired only to deliver this to you.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I tried to contact the company by dialing the phone number on the invoice. It didn’t work. It was either wrong or constantly busy. I checked their website for a phone number. No luck!&lt;/p&gt;

&lt;p&gt;Then I rushed back to the mall where I bought it. The salesgirl called her supervisor. I wasn’t the only one receiving a mattress in pieces that day.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“If you had called me right away, maybe I could have done something,”&lt;/em&gt; she told me. After telling me it was too late, she explained that my mattress was delivered from another city via another delivery company. &lt;em&gt;“Hey, but it isn’t that hard for you to assemble the mattress. It has some instructions inside.”&lt;/em&gt; Arrggg!&lt;/p&gt;

&lt;h2 id=&quot;the-lesson&quot;&gt;The lesson&lt;/h2&gt;

&lt;p&gt;If you promise something on your sales pages, deliver that promise. And make sure everyone involved understands what they need to do to make it a reality.&lt;/p&gt;

&lt;p&gt;Imagine KFC handing you raw chicken and telling you, &lt;em&gt;“You can fry it on your own. It comes with a step-by-step. It’s not that difficult.”&lt;/em&gt; You would never visit that restaurant again. Just like I’ll never trust that mattress company again.&lt;/p&gt;

&lt;p&gt;There’s no secret to business, just promise, then deliver.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Life Lesson I Learned Doom-Scrolling on Instagram</title>
   <link href="https://canro91.github.io/2025/07/29/InstagramLesson/"/>
   <updated>2025-07-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/29/InstagramLesson</id>
   <content type="html">&lt;p&gt;We all know doom-scrolling is the worst.&lt;/p&gt;

&lt;p&gt;But recently, I found myself doing precisely that on Instagram. Not every day has to &lt;a href=&quot;/2025/07/16/ProductivityTips/&quot;&gt;be productive&lt;/a&gt;, right?&lt;/p&gt;

&lt;p&gt;Out of hundreds of posts, one made me stop scrolling.&lt;/p&gt;

&lt;p&gt;It was the video of a girl cleaning a house while saying she wanted to keep cleaning houses for a living, even with two graduate degrees. It was probably a sketch promoting a book.&lt;/p&gt;

&lt;p&gt;But she said a line that resonated deeply with me:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“I don’t want to pretend I care about things I don’t care about.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wow! That made my head explode. That was me a couple of years ago, stuck in a 9-5 with decent pay and flexible hours, pretending to care. Spoiler alert: &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;I got sick and burned out&lt;/a&gt;. I didn’t burn out by doing too much, but from boredom and indifference. By doing things I didn’t care about.&lt;/p&gt;

&lt;p&gt;And that line made the scrolling session worth it. Yes, even doom-scrolling can teach valuable lessons life lessons.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Five Writing Tricks Behind &apos;Not Really on Purpose&apos;</title>
   <link href="https://canro91.github.io/2025/07/28/Chespirito/"/>
   <updated>2025-07-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/28/Chespirito</id>
   <content type="html">&lt;p&gt;I finished watching “Not Really on Purpose” (“Sin Querer Queriendo” in Spanish), a TV series based on the life of Roberto Gomez Bolaños.&lt;/p&gt;

&lt;p&gt;If you’ve never heard of him, he was a Mexican writer, TV producer, scriptwriter, and comedian. Best known as “Chespirito,” a nickname after “Little Shakespeare” in Spanish.&lt;/p&gt;

&lt;p&gt;Multiple generations from Latin America have watched or grown up watching his shows like &lt;em&gt;El Chavo del Ocho&lt;/em&gt; or &lt;em&gt;El Chapulin Colorado&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;“Not Really on Purpose” gives a glimpse of Chespirito’s early life and the story behind some of his most famous characters.&lt;/p&gt;

&lt;h2 id=&quot;i-watched-it-as-a-writer-and-i-noticed-these-devices&quot;&gt;I watched it as a writer, and I noticed these devices:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. The show starts with the second-to-last scene.&lt;/strong&gt; Roberto is getting dressed to appear as “El Chavo” at a benefit event in Colombia. He’s tired of the conflicts in his life and career.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. The show builds towards one main event:&lt;/strong&gt; El Chavo and his neighbors visit Acapulco. And a fun fact, that was the last time the original cast filmed together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. To keep us engaged, the shows uses time jumps&lt;/strong&gt; to show us Roberto’s early life, his romance and marriage, and the Acapulco episode’s production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. We’re shown some scenes twice.&lt;/strong&gt; Early on, we see some conflict. Roberto is flirting with another cast member while married with kids.&lt;/p&gt;

&lt;p&gt;Later, when the story has developed a bit more, the same scenes are replayed with more context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. The show finishes with some flashbacks&lt;/strong&gt; of early episodes and footage of the real Roberto on TV screens in the streets.&lt;/p&gt;

&lt;p&gt;It was funny to see how our favorite characters came to life and how Roberto found inspiration in his own kids. Chespirito didn’t just create TV shows, but a world of characters still alive in Latin America. One we’ll remember forever. Even it was not really on purpose.&lt;/p&gt;

&lt;p&gt;For other TV show breakdowns, see &lt;a href=&quot;/2025/05/18/HouseMD/&quot;&gt;Storytelling Secrets from One of My Favorite House M.D. Episodes&lt;/a&gt; and &lt;a href=&quot;/2025/03/13/SixTripleEight/&quot;&gt;What I Learned From Watching Netflix’s Six Triple Eight&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Weirdest Things That Happened While Building My Brand on LinkedIn</title>
   <link href="https://canro91.github.io/2025/07/27/LinkedInWeirdness/"/>
   <updated>2025-07-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/27/LinkedInWeirdness</id>
   <content type="html">&lt;p&gt;LinkedIn is a weird place.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;“I’m pleased to announce…“&lt;/em&gt; posts, the &lt;em&gt;“I have a new certificate…“&lt;/em&gt; updates, and the B2B lessons learned from every situation. &lt;em&gt;“I just proposed, here’s what it taught me about B2B sales.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We do crazy things to impress future employers online.&lt;/p&gt;

&lt;p&gt;Since last year, I’ve &lt;a href=&quot;/2025/06/15/LinkedIn/&quot;&gt;posted 300+ times&lt;/a&gt; to &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;grow on LinkedIn&lt;/a&gt;. And here are a few of the weirdest things I’ve seen:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1.&lt;/strong&gt; A lead generation coach went full sales mode, pitching me a funnel course… when I didn’t have anything to sell. &lt;em&gt;“Thanks, but… No thanks. Clicking Unfollow.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2.&lt;/strong&gt; A DM offering to make me the “face” during interviews. I’d have to take interviews and land jobs. Then someone else would show up to work. Maybe I’d end up participating in the North Korean engineers scheme. &lt;em&gt;“Block and report. Call the Internet police?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Random people liked one of my posts, then rushed to my DMs asking for a coffee chat. &lt;em&gt;“Sorry, not the best time now… or in the near future.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4.&lt;/strong&gt; Coaches offering me “free” help, just prepping the ground for a sales pitch. &lt;em&gt;“Let’s cut to the chase. What are you selling?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sure, LinkedIn might be a weird place. But I’ve met interesting people, landed one speaking gig, and made my first Internet money. And that’s why, no matter how weird it gets, I keep showing up.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What I Gained After 6 Years Blogging on dev.to</title>
   <link href="https://canro91.github.io/2025/07/26/DevToAnniversary/"/>
   <updated>2025-07-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/26/DevToAnniversary</id>
   <content type="html">&lt;p&gt;Six years ago, I wrote my first blog post… and heard crickets.&lt;/p&gt;

&lt;p&gt;Nobody was reading my blog. I wanted some traffic there. &lt;a href=&quot;/2025/06/16/BloggingExpectations/&quot;&gt;Some attention&lt;/a&gt;. I was playing the SEO game with keywords and answer posts. I prayed to the SEO gods to send readers to my blog.&lt;/p&gt;

&lt;p&gt;Those days, I found out about &lt;a href=&quot;https://dev.to/canro91&quot;&gt;dev.to&lt;/a&gt;, the new platform in town for coders.&lt;/p&gt;

&lt;p&gt;A better way to get readers? Create an account and repost there. So on July 23rd, 2019, I did it. I didn’t know where it would take me.&lt;/p&gt;

&lt;h2 id=&quot;some-vanity-metrics-before-i-move-on&quot;&gt;Some vanity metrics, before I move on.&lt;/h2&gt;

&lt;p&gt;In six years, I’ve written 176 posts.&lt;/p&gt;

&lt;p&gt;Some of them are dev.to “originals.” Others reposts. I wrote more posts, but I deleted the oldest in a moment of embarrassment. Yes, even I cringed at reading them. They were so bad.&lt;/p&gt;

&lt;p&gt;My posts on dev.to have received +114K views and 1.6K reactions. And I have +25K followers. Mostly bots or inactive users I think.&lt;/p&gt;

&lt;h2 id=&quot;but-devto-didnt-just-send-traffic&quot;&gt;But dev.to didn’t just send traffic.&lt;/h2&gt;

&lt;p&gt;Where was I? OK, what my dev.to account has done for me…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. It’s helped me connect with other members of the community.&lt;/strong&gt; I’ve had virtual coffees with other “devtoers.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. It’s given me confidence in my writing skills.&lt;/strong&gt; I went from deleting some posts in embarrassment to being featured in the Top7 more than once. That’s a sign my writing has improved. A win for &lt;a href=&quot;/2025/04/16/WritingChanges/&quot;&gt;taking my writing more seriously&lt;/a&gt; since last year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. It’s put my content in front of an audience.&lt;/strong&gt; Last year, &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;I burned out&lt;/a&gt; and &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;got laid off&lt;/a&gt;. Writing was my therapy. So I started to share my career stories and lessons. Writing helped me process &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;a rejection from a FAANG&lt;/a&gt;, for example. Seeing my posts resonating with a lot of people was so encouraging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. It gave me a small moment of virality.&lt;/strong&gt; Thanks to dev.to, I went viral for the first time ever. With this post: &lt;a href=&quot;https://dev.to/canro91/this-is-why-we-dont-test-private-methods-28ef&quot;&gt;This Is Why We Don’t Test Private Methods&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a single day, I got thousands of readers and dozens of downloads from &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad account&lt;/a&gt;. Shameless plug. Someone reshared it or it got syndicated somewhere. Dunno. I felt like an Internet coding celebrity… just for a few hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. It’s given me a chance of giving back to the coding community.&lt;/strong&gt; It’s helped me turn my stories into inspiration and some laughs for others. Like the best comment I’ve received: &lt;em&gt;“Made my day go from brain-f*cked to f*cking good.”&lt;/em&gt; That made my day and reminded me why I keep showing when it feels nobody is reading.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: AI, checklists, and cocktails</title>
   <link href="https://canro91.github.io/2025/07/25/FridayLinks/"/>
   <updated>2025-07-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/25/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;I thought I wouldn’t find another 4 since &lt;a href=&quot;/2025/07/23/WednesdayLinks/&quot;&gt;last Wednesday email&lt;/a&gt;. Here are 4 links for you today: (I couldn’t avoid adding the last one, not about coding though)&lt;/p&gt;

&lt;p&gt;#1. The whole point of AI is making coders faster. But &lt;a href=&quot;https://www.reuters.com/business/ai-slows-down-some-experienced-software-developers-study-finds-2025-07-10/&quot;&gt;here’s an study&lt;/a&gt; (4min) that shows AI makes some experienced developers slower.&lt;/p&gt;

&lt;p&gt;#2. Sure, AI is the buzzword these days. But &lt;a href=&quot;https://terriblesoftware.org/2025/07/14/what-doesnt-change/&quot;&gt;there are things that don’t change&lt;/a&gt; (4min). “AI amplifies what you already know.”&lt;/p&gt;

&lt;p&gt;#3. Something we coders should learn from pilots? &lt;a href=&quot;https://utcc.utoronto.ca/~cks/space/blog/sysadmin/ChecklistsAreHardButGood&quot;&gt;The value of checklists&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;#4. This one isn’t about coding, but… I found out there’s an official list of cocktails and &lt;a href=&quot;https://aaronson.org/blog/i-drank-every-cocktail&quot;&gt;a guy who drank them all&lt;/a&gt; (20min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/07/20/StandOutFromAI/&quot;&gt;how to stand out from AI content&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/06/22/SimplerNoteTaking/&quot;&gt;the simple method I use to replace my second brain&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>10 Reading Challenges for Book Lovers (Inspired by the &apos;I Drank Every Cocktail&apos; Guy)</title>
   <link href="https://canro91.github.io/2025/07/24/ReadingChallenges/"/>
   <updated>2025-07-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/24/ReadingChallenges</id>
   <content type="html">&lt;p&gt;Adam Aaronson drank every cocktail on the official list.&lt;/p&gt;

&lt;p&gt;Yes, there’s an official list of cocktails. And &lt;a href=&quot;https://aaronson.org/blog/i-drank-every-cocktail&quot;&gt;Adam drank them all&lt;/a&gt;. He started by handing the list to a bartender in his city and ended up at Satan’s Whiskers in London. Yes, that’s a bar.&lt;/p&gt;

&lt;p&gt;Quite an impressive adventure.&lt;/p&gt;

&lt;p&gt;Just like Adam tackled cocktails, what about book lovers? That story reminded me of &lt;a href=&quot;/2025/06/17/TenIdeas/&quot;&gt;a 10-idea list&lt;/a&gt; I wrote with 10 book reading challenges. If you want to try one, here they are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Join a book reading club&lt;/li&gt;
  &lt;li&gt;Read the last 25 Pulitzer winners&lt;/li&gt;
  &lt;li&gt;Read the last 25 Nobel Prize winners&lt;/li&gt;
  &lt;li&gt;Read your favorite writer’s complete works&lt;/li&gt;
  &lt;li&gt;Read a book from a new writer every month&lt;/li&gt;
  &lt;li&gt;Read one book per week. Or 52 books in a year&lt;/li&gt;
  &lt;li&gt;Take your own MBA by reading 12 business books in a year&lt;/li&gt;
  &lt;li&gt;Read 12 biographies of history’s most influential figures&lt;/li&gt;
  &lt;li&gt;Read James Altucher’s book recommendations. &lt;a href=&quot;https://archive.jamesaltucher.com/blog/books-saved-life/&quot;&gt;Full list here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Read 10 or 12 books released more than 50 years ago. &lt;a href=&quot;/2025/04/05/CulturalTutor/&quot;&gt;Idea from CulturalTutor&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Which one would you like to try first? For my challenge, I’m doing #4. I’m reading most of &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;’s books.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Wednesday Links about Google, Chrome, and TODOs</title>
   <link href="https://canro91.github.io/2025/07/23/WednesdayLinks/"/>
   <updated>2025-07-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/23/WednesdayLinks</id>
   <content type="html">&lt;p&gt;Hey. I just realized I already had 4 interesting links to share. I didn’t want to wait until next Friday to share them. So here they are.&lt;/p&gt;

&lt;p&gt;#1. I have to admit I fell into the specialization trap and started calling myself a “backend engineer.” I stopped trying to be a full stack developer. Maybe with AI, we’re witnessing &lt;a href=&quot;https://brodzinski.com/2025/07/renaissance-full-stack-developers.html&quot;&gt;the renaissance of full stack developers&lt;/a&gt; (10min) again.&lt;/p&gt;

&lt;p&gt;#2. The problem with Google AI Overview is we think their answers are accurate. Hey it’s Google. They must be building AI answers from all the pages they have indexed. Wrong. In fact, &lt;a href=&quot;https://davebarry.substack.com/p/death-by-ai&quot;&gt;Google AI Overview might kill you&lt;/a&gt; (7min)&lt;/p&gt;

&lt;p&gt;#3. Speaking of Google killing people, Chrome killed uBlock Origin. And if you want it back, you need to turn to Firefox. Here’s &lt;a href=&quot;https://kau.sh/blog/how-to-firefox/&quot;&gt;a good adoption guide&lt;/a&gt; (8min).&lt;/p&gt;

&lt;p&gt;#4. From Clean Code, I learned to avoid comments, like all comments. But there’s &lt;a href=&quot;https://sophiebits.com/2025/07/21/todos-arent-for-doing&quot;&gt;some value in TODO comments&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/07/19/ProductivitySecret/&quot;&gt;productivity secrets revealed by a real spy&lt;/a&gt; (2min) and &lt;a href=&quot;https://canro91.github.io/2025/07/21/Fluency/&quot;&gt;a language lessons I learned in an Asian restaurant&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next Friday, even if that’s only with one or two links.&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>4 Fresh Insights That Changed How I Think About Writing Books</title>
   <link href="https://canro91.github.io/2025/07/22/Books/"/>
   <updated>2025-07-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/22/Books</id>
   <content type="html">&lt;p&gt;I’ve decided to start a new writing adventure: writing a book.&lt;/p&gt;

&lt;p&gt;I’ve had to debunk lots of limiting beliefs.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“I need a publisher”&lt;/li&gt;
  &lt;li&gt;“I need 100% new material”&lt;/li&gt;
  &lt;li&gt;“Writing a book is hard.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wrong!&lt;/p&gt;

&lt;p&gt;After going down the rabbit-hole of searching how to write and self-publish a book, here’s what I’ve learned:&lt;/p&gt;

&lt;h2 id=&quot;1-a-hit-book-creates-demand-for-morebooks&quot;&gt;1. A hit book creates demand for more books.&lt;/h2&gt;

&lt;p&gt;After &lt;em&gt;Atomic Habits&lt;/em&gt;, there was an audience interested in habit creation. Then, more books about the same topic came out.&lt;/p&gt;

&lt;p&gt;A hit book isn’t necessarily competence. It’s signal of market interest.&lt;/p&gt;

&lt;h2 id=&quot;2-you-can-plagiarize-yourself&quot;&gt;2. You can plagiarize yourself.&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2025/06/24/Remixing/&quot;&gt;We can remix our own ideas&lt;/a&gt;. Credits to James Altucher’s podcast.&lt;/p&gt;

&lt;p&gt;A good post can become part of a book chapter. A series of posts can become a short book. A personal story can become an intro.&lt;/p&gt;

&lt;p&gt;That’s the strategy Yuval Noah Harari used to write &lt;em&gt;21 Lessons for the 21st Century&lt;/em&gt;. He compiled lots of disperse ideas into a book.&lt;/p&gt;

&lt;p&gt;More examples? &lt;em&gt;The Subtle Art of Not Giving a F*ck&lt;/em&gt; was a blog post. &lt;em&gt;The Psychology of Money&lt;/em&gt;, a series of posts.&lt;/p&gt;

&lt;p&gt;You see? We can repurpose our posts into books.&lt;/p&gt;

&lt;h2 id=&quot;3-a-books-job-is-to-tellstories&quot;&gt;3. A book’s job is to tell stories.&lt;/h2&gt;

&lt;p&gt;(Another lesson from James Altucher’s podcast.)&lt;/p&gt;

&lt;p&gt;Always be storytelling. Stories are the best way to make a message memorable.&lt;/p&gt;

&lt;p&gt;Our job is to tell stories, not to present facts - unless we’re writing a textbook or something.&lt;/p&gt;

&lt;h2 id=&quot;4-a-book-livesforever&quot;&gt;4. A book lives forever.&lt;/h2&gt;

&lt;p&gt;Your book doesn’t need to be a best-seller at launch. It can succeed anytime. And there’s plenty of time for that.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What a Chinese Couple Running a Restaurant Taught Me About Learning Languages</title>
   <link href="https://canro91.github.io/2025/07/21/Fluency/"/>
   <updated>2025-07-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/21/Fluency</id>
   <content type="html">&lt;p&gt;Yesterday, I revisited an Asian restaurant I hadn’t been to in years.&lt;/p&gt;

&lt;p&gt;It’s a family business in a neighborhood I used to live in. It’s still run by the same Chinese couple. And interestingly, their level of conversational Spanish is rather intermediate. Still the same as when I met them. Probably an A2, if you’re familiar with language proficiency levels.&lt;/p&gt;

&lt;p&gt;But they know enough Spanish to explain what’s in every dish, take your order, and take your payment. And that’s all they need.&lt;/p&gt;

&lt;p&gt;Their Spanish may be basic, but it’s enough to meet their daily needs. Do they speak Spanish? Absolutely. Maybe not about politics, philosophy, or the meaning of life.&lt;/p&gt;

&lt;p&gt;Too often we think fluency is about attending a language institute and getting straight As on an exam. But fluency isn’t about perfection.&lt;/p&gt;

&lt;p&gt;Fluency is about connection and communication. It’s about knowing the right words for the right moment. And the Chinese couple running the restaurant are completely fluent.&lt;/p&gt;

&lt;p&gt;Maybe you’re not opening a restaurant, but here’s &lt;a href=&quot;/2024/11/25/LearningALanguageForWork/&quot;&gt;how I’d learn a language for work from scratch&lt;/a&gt; and &lt;a href=&quot;/2025/05/03/LanguageHacks/&quot;&gt;10 hacks I’ve used to learn foreign languages faster&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Only Tip You Need to Stand Out From AI-Generated Content</title>
   <link href="https://canro91.github.io/2025/07/20/StandOutFromAI/"/>
   <updated>2025-07-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/20/StandOutFromAI</id>
   <content type="html">&lt;p&gt;You can smell AI-generated content from miles away.&lt;/p&gt;

&lt;p&gt;You can feel it. It’s &lt;a href=&quot;/2025/06/04/OpeningLines/&quot;&gt;the opening lines&lt;/a&gt;, the weird word choice.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“Delve”&lt;/li&gt;
  &lt;li&gt;“Ever-evolving”&lt;/li&gt;
  &lt;li&gt;“In our fast-paced world…”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;/2025/02/15/FightingAIContent/&quot;&gt;You can’t fight against it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently, I learned how to stand out from AI while listening to &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;’s podcast. I forgot to &lt;a href=&quot;/2025/06/22/SimplerNoteTaking/&quot;&gt;take notes&lt;/a&gt;, but he said something like this,&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To stand out from AI, we have to live an interesting life.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AI only remixes content it was trained on. AI can’t beat an authentic voice with impactful stories. We have to live our stories and pour them into our writing.&lt;/p&gt;

&lt;p&gt;My writing resonated more once I started sharing personal stories. They turned &lt;a href=&quot;/2025/04/16/WritingChanges/&quot;&gt;my writing from crickets to likes&lt;/a&gt;. So tell stories only you can tell. That’s how you truly stand out from the noise.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Productivity Secret for Getting Things Done—Revealed by a Spy</title>
   <link href="https://canro91.github.io/2025/07/19/ProductivitySecret/"/>
   <updated>2025-07-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/19/ProductivitySecret</id>
   <content type="html">&lt;p&gt;We think of guns, car chases, and James Bond when talking about spies.&lt;/p&gt;

&lt;p&gt;Productivity rarely comes to mind. But real espionage, as shown in TV shows like The Agency or Lioness, is full of chaos, multitasking, and shifting priorities.&lt;/p&gt;

&lt;p&gt;Thanks to Mr. YouTube algorithm, I stumbled upon an interview with a former American spy:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/X3vHJiPRLSo?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;He answers a ton of questions about espionage and psychology. But here are three battle-tested lessons about productivity I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. We all have to balance the same three resources&lt;/strong&gt;. Energy, money, and time. Two of them are limited. The other one, not so much. That’s why we should optimize for energy and time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Task saturation&lt;/strong&gt; is when you have more tasks than you can comfortably handle. When that happens, you become so overwhelmed, you stop thinking rationally. Think of how many tasks you can handle at the same time and subtract two. That’s your sweet spot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; When you’re task saturated, &lt;strong&gt;always tackle the task you can finish the fastest.&lt;/strong&gt; And then, the next fastest one and so on. That gives you a sense of progress and momentum.&lt;/p&gt;

&lt;p&gt;With those three lessons, you’re ready to get things done like a real spy. Don’t forget the cool gadgets and masks from Mission Impossible.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Selling yourself, ergonomic repos, and dead languages</title>
   <link href="https://canro91.github.io/2025/07/18/FridayLinks/"/>
   <updated>2025-07-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/18/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. Here’s &lt;a href=&quot;https://www.fldr.zip/blog/sell-yourself&quot;&gt;how to sell yourself&lt;/a&gt; (3min). It distills the tactics from the guy who landed and balanced multiple startup jobs at the same time. He must have done something right to land those jobs in the first place, right?&lt;/p&gt;

&lt;p&gt;#2. Want to build software quickly? Here’s &lt;a href=&quot;https://evanhahn.com/how-i-build-software-quickly/&quot;&gt;a guide on how to do it&lt;/a&gt; (12min).&lt;/p&gt;

&lt;p&gt;#3. When I hear “ergonomic,” I think of chairs or keyboards. But what about repositories? Here’s &lt;a href=&quot;https://pdx.su/blog/2025-07-10-make-your-repo-ergonomic/&quot;&gt;how to make repos ergonomic too&lt;/a&gt; (14min). It starts with the README file.&lt;/p&gt;

&lt;p&gt;#4. Like human languages, programming languages grow, evolve, and die. They borrow features from one another. Here are some of the &lt;a href=&quot;https://www.hillelwayne.com/post/influential-dead-languages/&quot;&gt;already dead but influential programming languages&lt;/a&gt; (20min). I used one of those in high school.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/07/14/CommAtWork/&quot;&gt;two tiny fixes to improve your communication at work&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/07/17/TechBeforeBreakfast/&quot;&gt;10 pieces of tech we use before breakfast without noticing them&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>10+ Pieces of Tech You Use Before Breakfast Without Even Realizing It</title>
   <link href="https://canro91.github.io/2025/07/17/TechBeforeBreakfast/"/>
   <updated>2025-07-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/17/TechBeforeBreakfast</id>
   <content type="html">&lt;p&gt;In many ways, we’re living in the best time in history.&lt;/p&gt;

&lt;p&gt;Yes, some leaders are suspiciously looking at each other with a finger ready to press a red button. But, centuries ago, people died from diseases we treat today with pills we can freely buy at any grocery store.&lt;/p&gt;

&lt;p&gt;Tech has advanced so much that we barely notice how many gadgets and breakthroughs we use before breakfast. I’ll be counting them as we go.&lt;/p&gt;

&lt;h2 id=&quot;waking-up&quot;&gt;Waking up&lt;/h2&gt;

&lt;p&gt;At 7:00 AM, your smartphone (#1) rings to wake you up.&lt;/p&gt;

&lt;p&gt;You don’t use an alarm clock anymore. A few hours before that, your AC (#2) automatically turned off after regulating your room’s temperature. Or maybe you have a fan, but I’m still counting #2.&lt;/p&gt;

&lt;p&gt;After waking up, you didn’t stop to notice you have electricity (#3) at home and a comfy mattress (#4). That used to be a privilege reserved for kings.&lt;/p&gt;

&lt;h2 id=&quot;in-the-bathroom&quot;&gt;In the bathroom&lt;/h2&gt;

&lt;p&gt;After waking up, without running away from hungry lions, you walk to your bathroom.&lt;/p&gt;

&lt;p&gt;You use the faucet and brush your teeth with clean water (#5). You do your “business” in a toilet (#6), using paper.&lt;/p&gt;

&lt;p&gt;Romans used, I mean shared, a sponge-tipped stick in public bathrooms. “Augustus, are you done with the sponge? It’s my time!” Soooo disgusting. Thanks, Marcus Aurelius for all your wisdom. But I don’t want to live in your time.&lt;/p&gt;

&lt;p&gt;And when you’re done, one button press. Poof, like magic, your “business” disappears. We’ve built complex plumbing to get rid of it (#7), that we don’t even care what happens after pressing that button.&lt;/p&gt;

&lt;h2 id=&quot;in-the-kitchen&quot;&gt;In the kitchen&lt;/h2&gt;

&lt;p&gt;A glass of water first thing after waking up?&lt;/p&gt;

&lt;p&gt;A company has collected, purified, and distributed water from who knows where to bring it to your home (#8). We’ve perfected engineering and chemistry to make that happen.&lt;/p&gt;

&lt;p&gt;To really wake up, a cup of coffee, right? You use a coffee machine (#9). Coffee beans grown in Latin America likely traveled to Switzerland to become capsules for your machine.&lt;/p&gt;

&lt;p&gt;Not into coffee? Sure. What about some tea? A water heater! Or a fresh orange juice? You have a juice maker for that.&lt;/p&gt;

&lt;p&gt;What about some eggs and toast with that coffee? You open your fridge (#10), then pull out your toaster from the counter (#11), and use your stove.&lt;/p&gt;

&lt;p&gt;And just to finish your morning routine, what about some vitamins and supplements? Packed with calcium, magnesium, zinc, and more, everything your body needs in one pill (#12).&lt;/p&gt;

&lt;p&gt;And there’s a whole lot of physics, electronics, engineering, global shipping, medicine, chemistry… just for breakfast. And we’re not even dreaming about the future with robotics, automation, and AI. We’re already in the future. It’s here&lt;/p&gt;

&lt;p&gt;OK, I told you about 10 pieces of tech, but I ended up counting 12. There’s probably more I forgot to count.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Productivity Tips That Really Work (They&apos;ll Make You Unstoppable)</title>
   <link href="https://canro91.github.io/2025/07/16/ProductivityTips/"/>
   <updated>2025-07-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/16/ProductivityTips</id>
   <content type="html">&lt;p&gt;About five years ago, I became obsessed with productivity.&lt;/p&gt;

&lt;p&gt;I devoured the entire Lifehacker site, looking for the best system and the perfect tool to finish my work. Pomodoro technique, Eisenhower matrix, inbox zero, &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;a todo.txt file&lt;/a&gt;. I tried them all.&lt;/p&gt;

&lt;p&gt;After testing some of those techniques and simplifying my system, I’ve found what really works, at least for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Follow your energy levels.&lt;/strong&gt; Work on the most important tasks when your energy is at its peak. For me, that’s in the morning, after breakfast and working out. Exercise or meditate to boost your focus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Protect your sacred hours.&lt;/strong&gt; When tackling your most important tasks, remove all distractions from your environment. 99% of the time, that means your smartphone. Even &lt;a href=&quot;/2024/12/13/KeepingPhonesAround/&quot;&gt;if you keep it around, it still distracts you&lt;/a&gt;. Yes, that’s backed by science.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Work in cycles of 90 minutes of work followed by 20 minutes of rest.&lt;/strong&gt; This is &lt;a href=&quot;/2025/06/18/PeakPerformance/&quot;&gt;a tip I recently learned&lt;/a&gt;, and I’ve been trying it since then. The brain needs that rest in between to work at its peak. Tweak those 90/20 to suit your natural rhythm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Follow the 5 minute rule.&lt;/strong&gt; When you don’t feel like working, start with a simple task and sustain it for 5 minutes. Or start with a task you can do the fastest. You need a small push to finish your more demanding work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Use a parking lot.&lt;/strong&gt; During your sacred hours, if you come up with new tasks, write them down (paper, app, whatever works) and do it later. You should still protect your deep work time.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>5 Ideas to Write Headlines That Make Readers Stop Scrolling</title>
   <link href="https://canro91.github.io/2025/07/15/Headlines/"/>
   <updated>2025-07-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/15/Headlines</id>
   <content type="html">&lt;p&gt;Headlines and opening lines get you 80% of the results.&lt;/p&gt;

&lt;p&gt;Because, based on Small Brevity, readers spend 17ms deciding if they keep reading or move on. So no matter how helpful your content is, nobody will click it if you write a poor headline.&lt;/p&gt;

&lt;p&gt;Want to gain readers? Master your headlines with these ideas:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;Study your favorite YouTuber’s titles&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/06/05/IrresistibleHeadlines/&quot;&gt;Follow this 3-step formula&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/05/12/WritingLessons/&quot;&gt;Use one of these emotions&lt;/a&gt;: fear, curiosity, desire.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/05/26/TitleDrivenCreation/&quot;&gt;Follow title-driven creation&lt;/a&gt;: Headline first, content comes second.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/06/08/SampleHeadlines/&quot;&gt;Rewrite the headlines of posts you read&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you write online, you’re really in the business of writing headlines. Online writing is headline writing. No headline = no readers.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Tiny Fixes That Could Transform Your Communication at Work</title>
   <link href="https://canro91.github.io/2025/07/14/CommAtWork/"/>
   <updated>2025-07-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/14/CommAtWork</id>
   <content type="html">&lt;p&gt;Did you join Software Engineering because you liked coding and time alone? Think again.&lt;/p&gt;

&lt;p&gt;You’ll spend more time in meetings than coding:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Daily stand-ups&lt;/li&gt;
  &lt;li&gt;1-on-1s&lt;/li&gt;
  &lt;li&gt;Team retros&lt;/li&gt;
  &lt;li&gt;Five minutes (that become hours) with another dev revisiting code you wrote a year ago, and you barely remember it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all these interactions, technical skills alone aren’t enough. You need strong communication skills.&lt;/p&gt;

&lt;p&gt;The best resource for that? &lt;em&gt;How to Win Friends and Influence People.&lt;/em&gt; Newer books exist, but this one is a true classic.&lt;/p&gt;

&lt;p&gt;Here are two ideas from that book that changed how I approach conversations at work:&lt;/p&gt;

&lt;h2 id=&quot;never-ever-ever-tell-anyone-theyre-wrong&quot;&gt;Never, ever, ever tell anyone they’re wrong.&lt;/h2&gt;

&lt;p&gt;That’s the worst way to start or end a conversation. And you won’t change the other person’s mind.&lt;/p&gt;

&lt;p&gt;At a previous job, I had the chance to apply that principle.&lt;/p&gt;

&lt;p&gt;We were working to connect our hotel solution to a third-party API using the PKCE flow. One developers read an outdated tutorial and wanted to implement it incorrectly.&lt;/p&gt;

&lt;p&gt;If I hadn’t read the book, I would have said, &lt;em&gt;“You’re wrong. Here’s how to do it… You, moron!”&lt;/em&gt; OK, I would have only thought the last part.&lt;/p&gt;

&lt;p&gt;But instead I said something like, &lt;em&gt;“Hey, maybe I read an outdated tutorial or something. Here’s what I found…“&lt;/em&gt; A few moments later, I got a “You’re right!” with a facepalm emoji.&lt;/p&gt;

&lt;p&gt;Avoid blaming. Instead, pretend you’re the one who might be wrong.&lt;/p&gt;

&lt;h2 id=&quot;be-careful-with-your-but&quot;&gt;Be careful with your “but.”&lt;/h2&gt;

&lt;p&gt;Often what comes after a “but” is something negative.&lt;/p&gt;

&lt;p&gt;And people remember more the last words they hear. So if your “but” comes last, that’s all what they will remember.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“That’s a good idea, but we will go over budget”&lt;/em&gt; is different from &lt;em&gt;“That’s a good idea, and if we stay within budget, it’ll be perfect.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Find ways to replace your “no, but…” with a “yes, and…”&lt;/p&gt;

&lt;p&gt;Ten years ago, I thought coding was just cracking symbols. It took me years to learn coding is also about being in countless calls and sync-ups: negotiating deadlines and sharing expectations with non-tech people.&lt;/p&gt;

&lt;p&gt;That’s why working on our communication skills is one of the 30 proven strategies in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=two-tiny-fixes-that-could-transform-your-communication-work&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Most Dangerous Problem With Using AI for Coding</title>
   <link href="https://canro91.github.io/2025/07/13/TheProblemWithAI/"/>
   <updated>2025-07-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/13/TheProblemWithAI</id>
   <content type="html">&lt;p&gt;There’s good laziness and bad laziness.&lt;/p&gt;

&lt;p&gt;One day, the VP of a company I was contracting with called me “lazy.” That was a compliment. You know the lazy that finds an easy way to solve a problem. The good lazy way.&lt;/p&gt;

&lt;p&gt;But AI is turning us into bad lazy. The “I don’t want to think” kind of lazy. And I don’t want that type.&lt;/p&gt;

&lt;p&gt;I’ve been experimenting with AI for my coding. When I sit down to code, I open Copilot on a browser to see what I can offload.&lt;/p&gt;

&lt;p&gt;Recently, I’ve been migrating a legacy Visual Basic app and I’ve used Copilot to &lt;a href=&quot;/2025/05/24/CodingWithAI/&quot;&gt;code faster&lt;/a&gt; by helping me with &lt;a href=&quot;/2025/06/19/CodingWithAI/&quot;&gt;boring tasks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The problem? Last week, I was stuck on a stupid problem: finding a value in a dictionary from a list of possible keys. Maybe I needed some rest, but I couldn’t think of a &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;LINQ query&lt;/a&gt; for that. I was so tempted to wake up the genie in the bottle for that. It felt like the easy way out.&lt;/p&gt;

&lt;p&gt;It’s so tempting to go directly to the AI and outsource our thinking.&lt;/p&gt;

&lt;p&gt;Just the other day, I found a coder desperate because &lt;a href=&quot;/2025/06/28/AIRecovery/&quot;&gt;he couldn’t code without AI&lt;/a&gt; anymore. If we’re not careful enough, any one of us could become that coder.&lt;/p&gt;

&lt;p&gt;AI is faster at generating code than we are. No doubt!&lt;/p&gt;

&lt;p&gt;But being a good coder isn’t about typing fast. It’s about teamwork, clear communication, and other skills that don’t show up in autocomplete.&lt;/p&gt;

&lt;p&gt;I’ve packed those lessons into my book: &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; It’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What Software Engineering Should Learn from Aviation</title>
   <link href="https://canro91.github.io/2025/07/12/CodingAsFlying/"/>
   <updated>2025-07-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/12/CodingAsFlying</id>
   <content type="html">&lt;p&gt;Flying is safe but… accidents happen.&lt;/p&gt;

&lt;p&gt;When they happen, it’s all over the news. But the thousands upon thousands of safe flights don’t make it to the headlines.&lt;/p&gt;

&lt;p&gt;The magic of the Internet took me to &lt;a href=&quot;https://admiralcloudberg.medium.com/&quot;&gt;Admiral Cloudberg&lt;/a&gt; on Medium. Each post breaks down a rare accident, tracing the failed part or procedure that triggered the disaster.&lt;/p&gt;

&lt;h2 id=&quot;after-every-accident-theres-an-investigation&quot;&gt;After every accident, there’s an investigation&lt;/h2&gt;

&lt;p&gt;After binge-reading some of Admiral Cloudberg’s deconstructions, what struck me was the meticulous investigation after every accident.&lt;/p&gt;

&lt;p&gt;A committee finds out exactly what happened and why. Their task is to find the root cause and the subsequent chain of events.&lt;/p&gt;

&lt;p&gt;After finding the root cause, they:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;release bulletins to manufacturers&lt;/li&gt;
  &lt;li&gt;update procedures and checklists&lt;/li&gt;
  &lt;li&gt;add the accident scenario to simulators to train pilots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Their goal isn’t to blame the captain or anyone else, but to prevent the same mistakes in future flights.&lt;/p&gt;

&lt;h2 id=&quot;coding-isnt-flying&quot;&gt;Coding isn’t flying&lt;/h2&gt;

&lt;p&gt;As software engineers, often we hear or say,&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“Something went wrong. We don’t know why. It hasn’t happened since then.”&lt;/li&gt;
  &lt;li&gt;“OK, let’s move on. If that happens again, we’ll take a deeper look.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine if we treated coding the same way:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Checklists to release code&lt;/li&gt;
  &lt;li&gt;Another checklist for incidents&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/12/28/BetterOnboardings/&quot;&gt;Clear onboarding processes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/03/10/AutomateCodeStyle/&quot;&gt;Automated best practices&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Guilty-free culture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Coding would be as safe and reliable as flying.&lt;/p&gt;

&lt;p&gt;We must adopt the same commitment to safety protocols and procedures to never let the same mistake happen twice.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Coding, calling, and networking</title>
   <link href="https://canro91.github.io/2025/07/11/FridayLinks/"/>
   <updated>2025-07-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/11/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Do you remember the first time you saw a computer? I do. And I originally wrote &lt;a href=&quot;/2025/04/15/FirstTimeISawAComputer/&quot;&gt;that story on my blog&lt;/a&gt; (2min). But this week, it went kind of “viral” when I reposted it on dev.to.&lt;/p&gt;

&lt;p&gt;Anyway, here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. AI can make us code faster. But &lt;a href=&quot;https://ordep.dev/posts/writing-code-was-never-the-bottleneck&quot;&gt;writing code has never been the bottleneck&lt;/a&gt; (3min). It’s everything around writing that code in the first place.&lt;/p&gt;

&lt;p&gt;#2. Curious about the backstory of some coding jargon? Here’s &lt;a href=&quot;https://quuxplusone.github.io/blog/2025/04/04/etymology-of-call/&quot;&gt;why we “call” functions&lt;/a&gt; (10min). Funny enough, we also say “invoke” a function and in Spanish, everyone associates “invoking” with ghosts.&lt;/p&gt;

&lt;p&gt;#3. Instead of forcing devs to use AI, here are &lt;a href=&quot;https://newsletter.manager.dev/p/stop-forcing-ai-tools-on-your-engineers&quot;&gt;some ideas for managers to adopt AI&lt;/a&gt; (7min) in their teams.&lt;/p&gt;

&lt;p&gt;#4. Also an introvert? Here’s &lt;a href=&quot;https://aginfer.bearblog.dev/how-to-network-as-an-introvert/&quot;&gt;a quick guide to networking&lt;/a&gt; (5min) for you. Not the networking type with cables and switches.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/07/09/GoingOnVacation/&quot;&gt;the “I just went on vacation” effect&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/07/06/SmallBets/&quot;&gt;4 lessons to start your first online business&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Branding Exercise That Felt Like Free Therapy</title>
   <link href="https://canro91.github.io/2025/07/10/FreeTherapy/"/>
   <updated>2025-07-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/10/FreeTherapy</id>
   <content type="html">&lt;p&gt;I didn’t know a branding exercise would make me reflect on my life.&lt;/p&gt;

&lt;p&gt;I’m taking a branding/writing course online. One of the exercises was to identify our main achievements, mistakes, and lessons to build our online presence around that.&lt;/p&gt;

&lt;p&gt;Here are some of my answers for the career-related questions.&lt;/p&gt;

&lt;h2 id=&quot;what-were-the-most-notable-things-that-happened-during-your-young-adulthood-20-30-years&quot;&gt;What were the most notable things that happened during your young adulthood (20-30 years)?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;I was fired from my first job&lt;/a&gt;. It was at a top company in my city. It taught me &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;there’s no truly safe place to work&lt;/a&gt;. Not sure if that’s a win or a loss.&lt;/li&gt;
  &lt;li&gt;I became a Senior SWE in one of the most well-known companies in the tech sector in my city.&lt;/li&gt;
  &lt;li&gt;For the first time in my life, I worked with a multinational company in the tech sector. It doubled my previous salary and &lt;a href=&quot;/2025/03/22/IsBurnoutInevitable/&quot;&gt;it burned me out&lt;/a&gt;, too. In that order.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;I overcame my burnout&lt;/a&gt; by myself and took a mini-sabbatical for my mental health.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-were-the-most-notable-things-that-happened-in-your-career&quot;&gt;What were the most notable things that happened in your career?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I learned English to land high-paying jobs. But I found out I like &lt;a href=&quot;/2025/05/03/LanguageHacks/&quot;&gt;learning languages&lt;/a&gt;. So I learned another two. French and quite a bit of Brazilian Portuguese.&lt;/li&gt;
  &lt;li&gt;I found out I’m a lifelong learner.&lt;/li&gt;
  &lt;li&gt;By accident, &lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;I started writing online&lt;/a&gt; and made my first dollars online.&lt;/li&gt;
  &lt;li&gt;I doubled my salary and took one year off.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-have-been-some-of-the-hardest-or-painful-moments-in-your-life&quot;&gt;What have been some of the hardest or painful moments in your life?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I hit rock-bottom. Got burned out, depressed, and stomach sick. In that order. All of that because I decided to keep a “good” job with a decent salary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-have-been-the-most-valuable-lessons-you-learned-in-life-and-how-did-you-learn-them&quot;&gt;What have been the most valuable lessons you learned in life and how did you learn them?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;We’re just a cog in a machine if we keep a “safe” job. I learned this when I got fired and laid off multiple times.&lt;/li&gt;
  &lt;li&gt;If I don’t come up with my own life plan, somebody else will give me one. I had to overcome my burnout to learn this.&lt;/li&gt;
  &lt;li&gt;I’m not my job and my job title. I should &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;diversify my joy&lt;/a&gt;. It shouldn’t come from a single place. Thanks, burnout for teaching me this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-have-been-your-biggest-mistakes-and-how-did-it-change-you&quot;&gt;What have been your biggest mistakes and how did it change you?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I stayed too long at stagnant jobs, expecting things to change without taking any action. It cost me years and thousands of dollars. One of the few things I regret. It made me take control of my career and life.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-transformations-have-you-gone-through-in-life-physicalfinancialemotionalspiritualetc&quot;&gt;What transformations have you gone through in life (physical/financial/emotional/spiritual/etc.)?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;After months of waking stressed and anxious, I recovered from burnout by &lt;a href=&quot;/2025/05/13/PrioritizeHealth/&quot;&gt;working on my health&lt;/a&gt; and writing again. Finally, after almost a year, I could wake up feeling fulfilled and accomplished.&lt;/li&gt;
  &lt;li&gt;I lost about 5 kg in the last year, thanks to reordering how I eat. It turned out to be &lt;a href=&quot;/2024/12/30/BestProductivityHack/&quot;&gt;the best productivity hack&lt;/a&gt; I’ve discovered.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-are-your-most-significant-achievements&quot;&gt;What are your most significant achievements?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I paid with my own money for a fancy family dinner.&lt;/li&gt;
  &lt;li&gt;I made my first internet money with coding courses and writing online.&lt;/li&gt;
  &lt;li&gt;After a layoff, I survived one year without a job, living off my savings and investments. It was a tough time, but I remember it as the season where I grew the most.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>The &quot;I Just Went on Vacation&quot; Effect in Dev Teams</title>
   <link href="https://canro91.github.io/2025/07/09/GoingOnVacation/"/>
   <updated>2025-07-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/09/GoingOnVacation</id>
   <content type="html">&lt;p&gt;After years of working in dev teams, I’ve learned one rule the hard way:&lt;/p&gt;

&lt;p&gt;Don’t ask someone leaving to finish a critical task, even if they’re the only one who knows how.&lt;/p&gt;

&lt;p&gt;Someone going on vacation, or worse, leaving the job already has their mind elsewhere. They’re already thinking about packing, buying tickets, or finishing paperwork.&lt;/p&gt;

&lt;p&gt;Even if they finish the task, something will go wrong after they’re gone. Maybe it’s a requirement change, an unexpected bug, or a new scenario nobody saw coming. And they’re the only one who knows how to handle it.&lt;/p&gt;

&lt;p&gt;Too late, they’re already gone. People who stayed will have a hard time catching up.&lt;/p&gt;

&lt;p&gt;I don’t know if that’s ever happened inside your teams. But I’ve seen it more than once. Let’s call it the “I Just Went on Vacation” effect. And make sure we don’t fall into it, especially if you’re the one leaving.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Only Productivity Tip That Actually Works (Forget Notion)</title>
   <link href="https://canro91.github.io/2025/07/08/TheOnlyProductivityTip/"/>
   <updated>2025-07-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/08/TheOnlyProductivityTip</id>
   <content type="html">&lt;p&gt;Notion dashboards. Second brain apps. Inbox zero. None of it matters if you miss one thing.&lt;/p&gt;

&lt;p&gt;Rest.&lt;/p&gt;

&lt;p&gt;Without a good night’s sleep, your fancy productivity system fails. You drag your feet, drink coffee, and fight the urge to nap anywhere.&lt;/p&gt;

&lt;p&gt;To work like the &lt;a href=&quot;/2025/06/18/PeakPerformance/&quot;&gt;top 1% of high-performers&lt;/a&gt;, work hard but rest harder. &lt;a href=&quot;/2025/01/29/ScheduleRest/&quot;&gt;Schedule rest&lt;/a&gt; like you schedule your most important meetings. Non-negotiable.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Ditched My Buy Me a Coffee Link—Here&apos;s What I&apos;m Doing Instead</title>
   <link href="https://canro91.github.io/2025/07/07/AskingForCoffee/"/>
   <updated>2025-07-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/07/AskingForCoffee</id>
   <content type="html">&lt;p&gt;I’ve never liked being asked for money on the street.&lt;/p&gt;

&lt;p&gt;When someone asks me for money, I ask why they don’t sell something instead.&lt;/p&gt;

&lt;p&gt;But I realized I was doing the same thing: holding up a digital sign next to a rusty can in the internet streets. That hit hard.&lt;/p&gt;

&lt;p&gt;Instead of asking for coffee, I now invite readers to &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;join Friday Links&lt;/a&gt;, where I share 4 curated resources about programming and promote my books and courses.&lt;/p&gt;

&lt;p&gt;I get it! When we’re starting, we don’t want to sound salesy, so we go with a “support me” as a call-to-action. We think we shouldn’t monetize creativity, as if artists or writers should starve.&lt;/p&gt;

&lt;p&gt;After binge-reading &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;Tim Denning&lt;/a&gt;, a digital writer with billions of views, I learned to ditch the “Buy Me a Coffee” link and offer something of value, like a newsletter. Turns out, that’s &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;the strategy millionaire creators swear byr&lt;/a&gt;. And now, I do too.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>4 Game-Changing Lessons to Launch Your First Online Business (from the Creator of Small Bets)</title>
   <link href="https://canro91.github.io/2025/07/06/SmallBets/"/>
   <updated>2025-07-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/06/SmallBets</id>
   <content type="html">&lt;p&gt;He turned a half-joking Tweet into a community of over 7,000 creators.&lt;/p&gt;

&lt;p&gt;His name is Daniel Vassallo. He was a Software Engineer with a cushy job at Amazon. But one day, after being tired of daily meetings and the lack of freedom, he decided to start a solo business.&lt;/p&gt;

&lt;p&gt;His journey went from building software to freelancing, selling info products, and eventually running &lt;a href=&quot;https://smallbets.com/&quot;&gt;the Small Bets community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After binge-watching some of his interviews on YouTube, like &lt;a href=&quot;https://www.youtube.com/watch?v=AdOHszp1Cu4&quot;&gt;this one with the Refactoring podcast&lt;/a&gt;, here are 4 lessons I’ve learned:&lt;/p&gt;

&lt;h2 id=&quot;1-find-an-easy-way-to-make-1000-dollars&quot;&gt;1. Find an easy way to make $1,000 dollars.&lt;/h2&gt;

&lt;p&gt;You don’t need a company or investors to begin.&lt;/p&gt;

&lt;p&gt;Start with a low-hanging fruit, a small project that makes you a few hundred or thousand bucks to build momentum.&lt;/p&gt;

&lt;p&gt;For Daniel’s first project, he tried to imitate the processes from his last coding job. Coming up with a yearly goal, splitting it into milestones, and working in sprints. That only made him burn through almost all of his savings.&lt;/p&gt;

&lt;p&gt;Don’t replicate your last full-time job. Find an easy project first.&lt;/p&gt;

&lt;h2 id=&quot;2-be-like-an-investor-but-without-being-one&quot;&gt;2. Be like an investor, but without being one.&lt;/h2&gt;

&lt;p&gt;You don’t need VC funding, but you need to think like an investor.&lt;/p&gt;

&lt;p&gt;An investor places bets on lots of startups. They know some of them will fail, but the one that succeeds will pay off all other investments.&lt;/p&gt;

&lt;p&gt;Think of your projects the same way. Don’t put all your effort into one basket. And like an investor, don’t get too attached to a particular project. They’re your cattle, not your pets.&lt;/p&gt;

&lt;p&gt;Run a portfolio of small bets so income doesn’t have to come from a single place.&lt;/p&gt;

&lt;h2 id=&quot;3-you-dont-need-a-big-master-business-plan&quot;&gt;3. You don’t need a big master business plan.&lt;/h2&gt;

&lt;p&gt;You don’t need a mission, vision, or values to start an online business.&lt;/p&gt;

&lt;p&gt;Your only goal might be to never return to a 9-5. That’s Daniel’s goal. That could be yours and mine too.&lt;/p&gt;

&lt;h2 id=&quot;4-the-ultimate-goal-is-freedom&quot;&gt;4. The ultimate goal is freedom.&lt;/h2&gt;

&lt;p&gt;The goal of starting a business is to have the freedom to work under your own terms.&lt;/p&gt;

&lt;p&gt;For Daniel, that means having no rigid schedule or routine, but working on the tasks with the biggest ROI every day. Or simply scrolling through Twitter/X looking for project inspiration.&lt;/p&gt;

&lt;p&gt;As a solopreneur or creator, you’re an investor, but of your own time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Five Inspiring Mantras to Help You Start Writing (and Stick With It)</title>
   <link href="https://canro91.github.io/2025/07/05/WritingMantras/"/>
   <updated>2025-07-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/05/WritingMantras</id>
   <content type="html">&lt;p&gt;In 2018, I wrote a blog post for the first time.&lt;/p&gt;

&lt;p&gt;I didn’t know what I was doing. I only wanted to get better at coding. And after years of trial and error and taking my writing seriously, I’ve developed my own writing mantras. Here they are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. If it can help one person, you’re safe to hit post.&lt;/strong&gt; Write about anything, as long as it’s helpful for at least one person. You don’t need to write to the masses. &lt;a href=&quot;/2024/12/24/WritingVoice/&quot;&gt;Just to one person&lt;/a&gt;. And that one person could be your past self.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Give something and give it fast.&lt;/strong&gt; Writing online is different from fiction writing. In a novella, you can describe every detail of the room where your scene is happening. But online, readers decide in milliseconds if they’ll keep reading or move on. &lt;a href=&quot;/2025/06/05/IrresistibleHeadlines/&quot;&gt;Nail your headlines&lt;/a&gt; and &lt;a href=&quot;/2025/05/09/BleedInTheFirstLine/&quot;&gt;opening lines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Write as if nobody is reading, and keep writing because you don’t know who’s reading.&lt;/strong&gt; Writing can be lonely, especially at the beginning. The cure? Write for your younger self. Write the tweets, posts, or books you would have liked to read two years ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Don’t wait to become an expert to write. Write to become one.&lt;/strong&gt; If you wait to become an expert, it will take you 10 years or more to write your first piece. Instead, share what you’re learning. &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show your work&lt;/a&gt;. The best way to learn is to teach, and the best way to teach is to write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. An intention makes you start but a system keeps you showing up.&lt;/strong&gt; It’s easy to start. Just drop a bunch of words into a text box and hit “Post.” The hard part? Doing it for years. For that, find a system to capture ideas and turn them into content, and one piece into many.&lt;/p&gt;

&lt;p&gt;In the end, it isn’t just intention. It’s the right attitude, a system, and the habit that keeps you writing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: The software squeeze, TDD, and ad-blockers</title>
   <link href="https://canro91.github.io/2025/07/04/FridayLinks/"/>
   <updated>2025-07-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/04/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. Five years ago, we only needed “Software Engineer” as a title on LinkedIn to be flooded with “life-changing” opportunities. People jumped in for the money. Tech was booming. But that bubble popped. We’re living in &lt;a href=&quot;https://newsletter.manager.dev/p/the-software-engineering-squeeze&quot;&gt;a software engineering squeeze&lt;/a&gt; (5min).&lt;/p&gt;

&lt;p&gt;#2. The most boring part of software projects? It’s when we’re just closing JIRA tickets instead of solving real problems. It’s when we’re in &lt;a href=&quot;https://thecynical.dev/posts/ticket-driven-development/&quot;&gt;Ticket-Driven Development&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;#3. As a coder, you don’t have to know every single acronym, framework, or tool. It’s fine to &lt;a href=&quot;https://www.thecoder.cafe/p/i-dont-know&quot;&gt;say I don’t know&lt;/a&gt; (3min). But I’ve learned to rephrame it as “I don’t know…yet”&lt;/p&gt;

&lt;p&gt;#4. Maybe this is a good excuse to buy augmented reality glasses to &lt;a href=&quot;https://www.tomshardware.com/maker-stem/engineer-creates-ad-block-for-the-real-world-with-augmented-reality-glasses-no-more-products-or-branding-in-your-everyday-life&quot;&gt;use them as ad-blockers&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/06/28/AIRecovery/&quot;&gt;a recovery guide for AI-dependent coders&lt;/a&gt; (5min) and &lt;a href=&quot;/2025/06/29/BestAdvice/&quot;&gt;the best 3 pieces of advice I’ve received&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>7 Surprisingly Simple Ideas That Changed My Life (And Could Change Yours Too)</title>
   <link href="https://canro91.github.io/2025/07/03/LifeChangingIdeas/"/>
   <updated>2025-07-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/03/LifeChangingIdeas</id>
   <content type="html">&lt;h2 id=&quot;1-if-you-feel-lost-in-life-start-by-working-on-your-health&quot;&gt;#1. If you feel lost in life, start by working on your health.&lt;/h2&gt;

&lt;p&gt;Last year, for the first time, I had no idea what to do with my life.&lt;/p&gt;

&lt;p&gt;I had just recovered from a stomach sickness. I was on my way out from a burnout season. And &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;a layoff caught me off guard&lt;/a&gt;. No side income, no next offer waiting for me. No plan B.&lt;/p&gt;

&lt;p&gt;I felt more lost than when I graduated high school. At that time, I believed in the “go to college, find a job, work hard” path in front of me. But this time? I realized that the default path was a lie.&lt;/p&gt;

&lt;p&gt;By accident or luck, I found the idea of &lt;a href=&quot;/2025/05/13/PrioritizeHealth/&quot;&gt;working on my health&lt;/a&gt; when I felt lost. And it worked. Slowly, it gave me clarity and momentum.&lt;/p&gt;

&lt;p&gt;Truth is, to change our lives we don’t need “passion” or a big master plan. We just need to start working on our health.&lt;/p&gt;

&lt;h2 id=&quot;2-do-something-for-your-body-mind-and-spirit-every-day&quot;&gt;#2. Do something for your body, mind, and spirit every day.&lt;/h2&gt;

&lt;p&gt;After deciding to work on my health, &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;I ditched my to-do list&lt;/a&gt; and focused on one single goal: working on my body, mind, and spirit every day.&lt;/p&gt;

&lt;p&gt;I found that idea in one of &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher’s books&lt;/a&gt;. Probably Choose Yourself.&lt;/p&gt;

&lt;p&gt;For me, it meant a workout session, writing 200 words, and a moment of silence every single day.&lt;/p&gt;

&lt;p&gt;For you, it could be going running, meditating, or painting. Whatever. But do something for your body, mind, and spirit every day.&lt;/p&gt;

&lt;p&gt;We’re truly healthy when the three of them are healthy. Work on each one every day. That’s the easiest recipe for a happy life.&lt;/p&gt;

&lt;h2 id=&quot;3-save-50-of-your-income&quot;&gt;#3. Save 50% of your income&lt;/h2&gt;

&lt;p&gt;Credit goes to an ex-coworker from &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;my first coding job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One day, out of the blue, he came close to my desk and shared this unsolicited advice. For free.&lt;/p&gt;

&lt;p&gt;He told me:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Cesar, imagine you only make half of your salary. Save and invest the other half. Sooner rather than later, you will buy your own house.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the best piece of advice I’ve ever received. Since then, I started to save. Sometimes 50%. Other times, less.&lt;/p&gt;

&lt;h2 id=&quot;4-learn-a-second-or-third-language&quot;&gt;#4. Learn a second (or third) language&lt;/h2&gt;

&lt;p&gt;I learned English to make my CV attractive and land high-paying jobs.&lt;/p&gt;

&lt;p&gt;But learning English meant more than high-paying jobs. It opened a whole new world: friendships, traveling, books… And you’re reading this because one day my mom’s best friend told her to sign me up for English classes.&lt;/p&gt;

&lt;p&gt;For me, it was English. For you, maybe Arabic for work in the Middle East or Spanish to join a rising startup in LatAm. A new life is waiting for you behind a new language.&lt;/p&gt;

&lt;h2 id=&quot;5-if-you-dont-read-you-wont-learn-anything&quot;&gt;#5. If you don’t read, you won’t learn anything.&lt;/h2&gt;

&lt;p&gt;I don’t remember much of my philosophy class in high school.&lt;/p&gt;

&lt;p&gt;But I won’t forget our teacher’s mantra. He repeated it all the time:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“If you don’t read, you won’t learn anything.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many years later, I stumbled upon him at a mall and told him I still remembered his quote. He told me the second part:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“If you don’t read, you won’t have anything to talk about.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had to wait more than 10 years to learn the full line.&lt;/p&gt;

&lt;p&gt;Read one page or 10 or 15, but read something every day.&lt;/p&gt;

&lt;h2 id=&quot;6-start-a-blog&quot;&gt;#6. Start a blog&lt;/h2&gt;

&lt;p&gt;Apart from learning English, writing is one of the most helpful skills for my professional career.&lt;/p&gt;

&lt;p&gt;I made my first $100 online by sharing my words in a corner of the Internet.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;Start a blog&lt;/a&gt; or write tweets. Just write. It will teach you to think clearly and it can make you some money in the meantime.&lt;/p&gt;

&lt;h2 id=&quot;7-the-minute-you-learn-something-teach-it&quot;&gt;#7. The minute you learn something, teach it.&lt;/h2&gt;

&lt;p&gt;I found this idea in &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt; by Austen Kleon.&lt;/p&gt;

&lt;p&gt;You don’t need a lecture hall or scientific papers to teach. Simply share it with anyone over the phone. Or write it anywhere online. That’s what has powered my online presence since I wrote my first blog post back in 2018. Sharing what I learn has not only helped others, it has changed my own life.&lt;/p&gt;

&lt;p&gt;I expand these ideas, and cover more, in &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=surprisingly-simple-ideas-that-changed-my-life-could-change-yours&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. Real change doesn’t come from master plan or New Year’s resolutions, but from tiny daily actions.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Trick That Keeps Me Writing When Nobody&apos;s Watching</title>
   <link href="https://canro91.github.io/2025/07/02/Victories/"/>
   <updated>2025-07-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/02/Victories</id>
   <content type="html">&lt;p&gt;Writing isn’t hard. It’s doing it when nobody is reading.&lt;/p&gt;

&lt;p&gt;I started &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;reviving my LinkedIn account&lt;/a&gt; last year. At first, I only got one or two reactions from friends and ex-coworkers.&lt;/p&gt;

&lt;p&gt;To keep myself showing up day after day, I started a “wins” folder. That’s an idea I got from “Steal Like an Artist.” Every time something good happens, like an encouraging comment or extra likes, I’d screenshot it for motivation.&lt;/p&gt;

&lt;p&gt;This last week, I added another screenshot to it. It was a comment on my reposted Medium post about &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;writing a book for our future grandkids&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“I never thought of writing as a gift for future generations, but now I want to document everything. This really shifted something in me!”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That comment made my day. I managed	to change a stranger’s view on something with my words. And that was more rewarding than any of the other comments I had received recently.&lt;/p&gt;

&lt;p&gt;Collect and celebrate your small victories, too. They make your day and keep you going.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What It Means to Take Creative Work Seriously</title>
   <link href="https://canro91.github.io/2025/07/01/SomethingSeriously/"/>
   <updated>2025-07-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/07/01/SomethingSeriously</id>
   <content type="html">&lt;p&gt;It’s clear when you’re taking a relationship seriously.&lt;/p&gt;

&lt;p&gt;You spend time with your loved one and stop looking for somebody else.&lt;/p&gt;

&lt;p&gt;But what about a creative project? What does taking writing, painting, or coding seriously mean?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;I started writing in 2018&lt;/a&gt;, but I didn’t treat it seriously until 2024.&lt;/p&gt;

&lt;p&gt;For me, taking my writing seriously meant:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/12/14/Consistency/&quot;&gt;Showing up consistently&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Studying other writers’ work&lt;/li&gt;
  &lt;li&gt;Putting my writing in front of others&lt;/li&gt;
  &lt;li&gt;Challenging myself out of my comfort zone&lt;/li&gt;
  &lt;li&gt;Writing about other subjects apart from coding&lt;/li&gt;
  &lt;li&gt;Hanging around other writers online&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;Buying my first writing course&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Listening to feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With those actions, writing stopped being something I did only in my spare time and when inspiration struck to become part of my everyday rhythm. They help me build the confidence to &lt;a href=&quot;/2025/01/23/CallingYourselfAWriter/&quot;&gt;call myself a “writer.”&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taking a creative project seriously means committing it by showing up and working on improving your skills.&lt;/p&gt;

&lt;p&gt;Tom Critchlow, with his post &lt;a href=&quot;https://tomcritchlow.com/2025/06/27/taking-blogging-seriously/&quot;&gt;Taking Blogging Seriously&lt;/a&gt;, got me thinking about all this.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Three Rules for Starting a New Creative Project</title>
   <link href="https://canro91.github.io/2025/06/30/CreativeProjects/"/>
   <updated>2025-06-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/30/CreativeProjects</id>
   <content type="html">&lt;p&gt;Search YouTube for journaling or note-taking, and you’ll almost certainly land on Field Notes.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fieldnotesbrand.com/&quot;&gt;Field Notes&lt;/a&gt; makes pocket-sized notebooks. Yes, the old-style pen-and-paper kind of notebooks.&lt;/p&gt;

&lt;p&gt;I have to confess that I don’t use them (yet?). For &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;my 10 bad idea lists&lt;/a&gt;, I use a cheap, ring-bound notepad that fits in my pocket. Still, their story resonated a lot with me.&lt;/p&gt;

&lt;p&gt;While reading how the &lt;a href=&quot;https://www.fastcompany.com/91352848/field-notes-cult-notebook-started-out-as-a-side-project&quot;&gt;two creators started Field Notes&lt;/a&gt;, I found out they had a simple framework for starting creative projects:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It should make money&lt;/li&gt;
  &lt;li&gt;It should be something you’re proud of&lt;/li&gt;
  &lt;li&gt;It should teach you something&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For them, Field Notes checked the three boxes. And those three rules work for a design studio and for any creative project: a side hustle, newsletter, or book. OK, we can negotiate the first one a little.&lt;/p&gt;

&lt;p&gt;If you’re starting a creative project, &lt;a href=&quot;/2024/11/21/PutYourFingerprint/&quot;&gt;use your fingerprints&lt;/a&gt; and &lt;a href=&quot;/2025/01/02/SturgeonLaw/&quot;&gt;don’t forget about this law&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The 3 Best Pieces of Unsolicited Advice I&apos;ve Ever Received</title>
   <link href="https://canro91.github.io/2025/06/29/BestAdvice/"/>
   <updated>2025-06-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/29/BestAdvice</id>
   <content type="html">&lt;p&gt;You know that saying, “Don’t give advice unless asked?”&lt;/p&gt;

&lt;p&gt;Well, some people ignored it and gave it anyway. Here are the best pieces of advice I’ve received:&lt;/p&gt;

&lt;h2 id=&quot;from-high-school&quot;&gt;From high school:&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Read, because if you don’t read, you won’t learn anything.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was from my philosophy teacher in high school. He said it all the time. I don’t remember much from his class, but for some reason that line has stayed with me.&lt;/p&gt;

&lt;p&gt;If I still remember that years later, he did his job.&lt;/p&gt;

&lt;h2 id=&quot;from-my-first-job&quot;&gt;From my first job:&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Imagine you make half of your salary, save the other half.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A senior co-worker at &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;my first coding job&lt;/a&gt; gave me that totally unexpectedly. That lesson stuck with me so much, I couldn’t resist adding it to my &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot;&gt;an email course&lt;/a&gt; I created years later.&lt;/p&gt;

&lt;h2 id=&quot;from-my-last-full-time-job&quot;&gt;From my last full-time job:&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Diversify your career.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was from &lt;a href=&quot;/2024/11/26/FindingMentors/&quot;&gt;one of my not-mentors&lt;/a&gt;. I reached out to him when I wasn’t sure about where to take &lt;a href=&quot;/2025/03/04/CareerLessonIWishIKnew/&quot;&gt;my software engineering career&lt;/a&gt;. We had many 1-on-1s. That’s what I remember the most. It was good advice. I didn’t follow it. &lt;a href=&quot;/2025/03/22/IsBurnoutInevitable/&quot;&gt;I burned out&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Quick Recovery Guide for AI-Dependent Coders</title>
   <link href="https://canro91.github.io/2025/06/28/AIRecovery/"/>
   <updated>2025-06-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/28/AIRecovery</id>
   <content type="html">&lt;p&gt;Technology makes us lazy.&lt;/p&gt;

&lt;p&gt;That’s not an opinion but a fact. We can’t do mental math, find addresses, or memorize phone numbers anymore. That’s the problem with relying too much on a piece of tech. Smartphones, I’m looking at you.&lt;/p&gt;

&lt;p&gt;The same thing happens in coding, with AI and vibe-coding.&lt;/p&gt;

&lt;p&gt;I’m guilty too. I’ve been &lt;a href=&quot;/2025/06/19/CodingWithAI/&quot;&gt;experimenting with AI&lt;/a&gt; to offload my plate of boring tasks. And when I can’t think of an answer immediately, I’m tempted to go straight to the genie in the bottle to grant me a coding wish.&lt;/p&gt;

&lt;p&gt;And I’m not alone in this. Recently, I found this question on &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1ll2vzs/how_to_stop_relying_on_chatgpt/&quot;&gt;Reddit&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“It’s been a while since I coded on C#/Unity so I’m very rusty on the concepts and relying too much on ChatGPT makes me feel like I haven’t learned anything and can’t write code on my own without doing basic mistakes… How do I learn everything back? Isn’t there a way to refresh my mind? A really good video on YouTube or something? I want to stop using AI and code on my own.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the original poster and anyone else who wants to break free from AI, here are 10 ideas to try:&lt;/p&gt;

&lt;h2 id=&quot;0-ban-ai&quot;&gt;0. Ban AI.&lt;/h2&gt;

&lt;p&gt;Think of &lt;a href=&quot;/2025/03/31/AIAndCalculators/&quot;&gt;AI as calculators in math classes&lt;/a&gt;. You can’t use them until you know the procedure you want to automate by hand.&lt;/p&gt;

&lt;p&gt;Like any mom disciplining her kid, “No more AI until you do your homework…”&lt;/p&gt;

&lt;h2 id=&quot;1-study-your-main-language-syntax&quot;&gt;1. Study your main language syntax.&lt;/h2&gt;

&lt;p&gt;Get to know the syntax of your language of choice: write variables, functions, loops, classes…&lt;/p&gt;

&lt;p&gt;For that, grab a textbook or watch a &lt;em&gt;“all you need to know about X in 4 hours”&lt;/em&gt; YouTube videos. But &lt;a href=&quot;/2024/11/02/PassiveLearning/&quot;&gt;don’t just passively consume them&lt;/a&gt;, recreate the examples and projects from them. By typing them out. No Control C + Control V.&lt;/p&gt;

&lt;h2 id=&quot;2-know-your-standard-library&quot;&gt;2. Know your standard library.&lt;/h2&gt;

&lt;p&gt;Get familiar with your standard library:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Write a variable and see what your editor or IDE suggests.&lt;/li&gt;
  &lt;li&gt;What methods can you use with that type?&lt;/li&gt;
  &lt;li&gt;Look at their signature and docstring.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-study-sql&quot;&gt;3. Study SQL.&lt;/h2&gt;

&lt;p&gt;No matter how powerful ORMs are, we can’t escape from SQL.&lt;/p&gt;

&lt;p&gt;We’ve had SQL since the early 70s and chances are we’re still using SQL another decade or two.&lt;/p&gt;

&lt;p&gt;Learn to create tables, write queries aggregating results, and learn about JOINs. Download a light version of the StackOverflow database and play with it, if you want realistic examples.&lt;/p&gt;

&lt;h2 id=&quot;4-build-a-toy-project-from-scratch&quot;&gt;4. Build a toy project from scratch.&lt;/h2&gt;

&lt;p&gt;OK, I’m not talking about reinventing the wheel to write your own text editor or something.&lt;/p&gt;

&lt;p&gt;I’m talking about building a recipe catalog, a todo app, or a CLI wrapper around a free API. And build it from scratch: right click, then create new folder in your editor or IDE, and so on. It will teach you a lot.&lt;/p&gt;

&lt;h2 id=&quot;5-find-your-own-answers&quot;&gt;5. Find your own answers.&lt;/h2&gt;

&lt;p&gt;When you get an error message (you will if you follow #4), resist the urge of going back to AI or simply asking a friend.&lt;/p&gt;

&lt;p&gt;Try to figure out errors and exceptions on your own. Start by googling the error message. There’s a thing called Google that finds web pages with answers to our questions. Sure, it’s old-school but it builds real muscle. Remember, AI is still banned. (See #0)&lt;/p&gt;

&lt;h2 id=&quot;6-learn-the-most-common-data-structures&quot;&gt;6. Learn the most common data structures.&lt;/h2&gt;

&lt;p&gt;80% of the time, you’ll only need lists and dictionaries. But there are more data structures of course.&lt;/p&gt;

&lt;p&gt;Learn to use them and how to implement them. You won’t have to implement them from scratch at your daily job, but it will stretch your problem-solving muscles.&lt;/p&gt;

&lt;h2 id=&quot;7-study-a-textbook-on-math-for-computer-science&quot;&gt;7. Study a textbook on Math for Computer Science.&lt;/h2&gt;

&lt;p&gt;Unless you’re working on niche domains, you won’t need advanced Math.&lt;/p&gt;

&lt;p&gt;But grab a book on Discrete Math (or Math for Computer Science) and study a chapter or two. Again, to sharpen your thinking.&lt;/p&gt;

&lt;h2 id=&quot;8-practice-rubber-duck-debugging&quot;&gt;8. Practice rubber-duck debugging.&lt;/h2&gt;

&lt;p&gt;You will get stuck a lot. That’s a feature of being a coder, not a bug.&lt;/p&gt;

&lt;p&gt;When that happens,&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Grab pen and paper&lt;/li&gt;
  &lt;li&gt;Go through your program line by line&lt;/li&gt;
  &lt;li&gt;Talk out loud&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;9-read-the-official-documentation&quot;&gt;9. Read the official documentation.&lt;/h2&gt;

&lt;p&gt;Pull out the Mozilla’s Web Docs, Microsoft Learn, and any other official source for your language of choice, and not only read it, but come up with your own examples and think of how you can use what you’re reading in your own code.&lt;/p&gt;

&lt;p&gt;AI is a blessing for learning. Ask any veteran who learned from reference manuals, language specifications, and magazines, they’ll tell you. Just don’t let AI think for you. OK, let’s slightly lift the AI ban, &lt;a href=&quot;/2025/03/24/NewCodersAndAI/&quot;&gt;don’t use it to generate code&lt;/a&gt;. Use it as your copilot, not as your captain.&lt;/p&gt;

&lt;p&gt;To help you build hype-proof skills, I wrote &lt;strong&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=quick-recovery-guide-aidependent-coders&quot;&gt;Street-Smart Coding&lt;/a&gt;&lt;/strong&gt;. The roadmap I wish I had on my journey from junior/mid-level to senior.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Negativity, Git Notes, and hating your manager</title>
   <link href="https://canro91.github.io/2025/06/27/FridayLinks/"/>
   <updated>2025-06-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/27/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. I was wrong to think I had to point out everything “wrong” at work. I had good intentions. But it only gave me a reputation as someone problematic. Stay away from negativity. That’s &lt;a href=&quot;https://brooker.co.za/blog/2025/06/20/career.html&quot;&gt;this developer’s best career advice&lt;/a&gt; (4min)&lt;/p&gt;

&lt;p&gt;#2. I have never used Git Notes. Yes, that’s a Git feature. But it seems to be &lt;a href=&quot;https://tylercipriani.com/blog/2022/11/19/git-notes-gits-coolest-most-unloved-feature/&quot;&gt;the coolest and most unloved Git feature&lt;/a&gt; (5min)&lt;/p&gt;

&lt;p&gt;#3. I don’t think I’ve ever “hated” one of my managers. And I’ve had managers who were amazing and some who didn’t know what they were doing. But here’s &lt;a href=&quot;https://terriblesoftware.org/2025/06/24/why-engineers-hate-their-managers-and-what-to-do-about-it/&quot;&gt;why we engineers (sometimes) hate our managers&lt;/a&gt; (7min).&lt;/p&gt;

&lt;p&gt;#4. Despite all Internet advice about surviving the AI trend, &lt;a href=&quot;https://daedtech.com/surviving-the-great-commoditizer-stop-getting-good-at-chatgpt/&quot;&gt;we should stop getting good at ChatGPT&lt;/a&gt; (20min). Because &lt;em&gt;to get “good” at using ChatGPT to execute your tradecraft is thus to get good at commoditizing your own labor and your craft.&lt;/em&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/06/26/OddPairs/&quot;&gt;how to split an array into odd pairs (an interview exercise)&lt;/a&gt; (6min) and &lt;a href=&quot;/2025/06/21/ColdEmail/&quot;&gt;some ideas to avoid being ghosted when emailing busy people&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>How to Split an Array Into Odd Pairs (An Interview Exercise)</title>
   <link href="https://canro91.github.io/2025/06/26/OddPairs/"/>
   <updated>2025-06-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/26/OddPairs</id>
   <content type="html">&lt;p&gt;Here’s an exercise that I may or may not have found… or been asked to solve.&lt;/p&gt;

&lt;p&gt;Off the top of my head, the exercise might or might not have looked like this,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Let’s start with an array of integers of even size. Return if the array can be divided into N/2 pairs where each pair adds up to an odd number and those N/2 pairs contain every element of the given array.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[1, 2, 3, 4]&lt;/code&gt; passes those two conditions. Here are its pairs: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1, 2)&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(3, 4)&lt;/code&gt;. Their sum is an odd number.&lt;/p&gt;

&lt;p&gt;But &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[1, 3, 5, 7]&lt;/code&gt; doesn’t pass our two conditions. Each pair adds up to an even number.&lt;/p&gt;

&lt;h2 id=&quot;cheating-with-stackoverflow-and-copilot&quot;&gt;Cheating with StackOverflow and Copilot&lt;/h2&gt;

&lt;p&gt;Let’s create a producer-like method to generate all pairs, and then filter them out based on our two conditions.&lt;/p&gt;

&lt;p&gt;I’d like to write something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasFunnyPairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pais&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsOdd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsEachElementInExactlyOnePair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First, generate all pairs with an odd sum, then form N/2-size combinations, filtering out those that don’t include every element of the array.&lt;/p&gt;

&lt;p&gt;Since, we’re already in the future and I’m not in front of a whiteboard and an interviewer, I’m cheating with StackOverflow and Copilot.&lt;/p&gt;

&lt;h2 id=&quot;generating-k-combinations&quot;&gt;Generating k-Combinations&lt;/h2&gt;

&lt;p&gt;Here’s &lt;a href=&quot;https://stackoverflow.com/a/64414875&quot;&gt;a StackOverflow answer&lt;/a&gt; with a recursive method in JavaScript to choose k-combinations of an array,&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flatMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I asked Copilot to translate it to C#. After some tweaking, we have this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CombinationsInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CombinationsInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPrefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CombinationsInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With that method, we can write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pairs()&lt;/code&gt; to generate a list of tuples, representing each pair,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;finding-if-each-element-is-present-in-only-one-pair&quot;&gt;Finding if each element is present in only one pair&lt;/h2&gt;

&lt;p&gt;The methods &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsOdd()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sum()&lt;/code&gt; are easy. The one left is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsEachElementInExactlyOnePair()&lt;/code&gt;. Copilot to the rescue again,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsEachElementInExactlyOnePair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Count occurrences of elements in pairs&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Check if each element appears exactly once&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, after some gluing and turning some of those methods into extension methods, here’s the final solution,&lt;/p&gt;

&lt;p&gt;On a Helpers.cs file,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HereIGo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Helpers&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CombinationsInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CombinationsInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPrefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CombinationsInternal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsOdd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsEachElementInExactlyOnePair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Count occurrences of elements in pairs&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Check if each element appears exactly once&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elementCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On a Main.cs file,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HereIGo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OddPairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsOdd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AllExactlyInOnePair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;OddPairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;OddPairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;OddPairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;OddPairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;AI has changed the the tech interview landscape for good. The other day, &lt;a href=&quot;/2025/03/20/AISolvingAnExercise/&quot;&gt;I asked AI to solve another interview exercise&lt;/a&gt;.  And I was surprised. And more recently, I’ve used Copilot to help me with &lt;a href=&quot;/2025/06/19/CodingWithAI/&quot;&gt;these 4 boring coding tasks&lt;/a&gt;. Yes, AI isn’t going anywhere. So &lt;a href=&quot;/2025/01/21/AIAdaptation/&quot;&gt;we as coders have to adapt&lt;/a&gt;, like we have always done.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Most Challenging Managers to Work With (And What to Do About It)</title>
   <link href="https://canro91.github.io/2025/06/25/Managers/"/>
   <updated>2025-06-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/25/Managers</id>
   <content type="html">&lt;p&gt;I’ve changed my mind about what a good manager is.&lt;/p&gt;

&lt;p&gt;At one past job, I worked on short-term projects. Every six months or so, I had new projects with a new team leader and sometimes a new team.&lt;/p&gt;

&lt;p&gt;I worked with all kinds of managers. From those who still coded to others who hadn’t touched code in years.&lt;/p&gt;

&lt;p&gt;The most challenging ones? The newly promoted team members.&lt;/p&gt;

&lt;p&gt;In times of uncertainty, which is pretty much all the time, they fell back on what they know best: coding. And they forgot that as managers, &lt;a href=&quot;/2025/01/08/BeingATeamLeader/&quot;&gt;they aren’t the best coders anymore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In those situations, the best thing to do is follow the advice from &lt;a href=&quot;https://terriblesoftware.org/2025/06/24/why-engineers-hate-their-managers-and-what-to-do-about-it/&quot;&gt;Why Engineers Hate Their Managers&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;If you’re an engineer frustrated with your manager, consider that they might be drowning too—sometimes the best thing you can do is have an honest conversation about what you both need to succeed.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The day before, they were just teammates who got a quick promotion, tapped on their shoulder with no training or clear expectations. Just, “You’re a manager now. Figure it out. See you at the next performance review.”&lt;/p&gt;

&lt;p&gt;I used to think a good manager was a still-technical team member making code-level decisions. Now, a good manager is simply someone I’d like to work with again.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Remix Your Best Content Ideas—Like a Hit Song</title>
   <link href="https://canro91.github.io/2025/06/24/Remixing/"/>
   <updated>2025-06-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/24/Remixing</id>
   <content type="html">&lt;p&gt;If you’re like me, you’ve listened to your favorite songs plenty of times.&lt;/p&gt;

&lt;p&gt;Not just often, but over and over, in different versions. A live version during a concert, a studio version with a string quartet, or a duo version with another well-known artist.&lt;/p&gt;

&lt;p&gt;If singers do it, why not do the same thing in our content?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/03/11/BloggingTips/&quot;&gt;A good post&lt;/a&gt; can become an email for your newsletter or part of a book chapter. The message stays the same, but presented in a new format for a new audience. And that makes it different.&lt;/p&gt;

&lt;p&gt;As Robert Birming put it on &lt;a href=&quot;https://birming.com/2025/06/22/blog-recycling/&quot;&gt;Blog Recycling&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Dig up an old post and rewrite it from your current perspective. It’s not cheating, and it’s not copying. It’s growth.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s like singers giving a popular song a fresh arrangements, just like we can to our best posts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Reasons Why Listicles Rule Online (And Crush It on Social Media)</title>
   <link href="https://canro91.github.io/2025/06/23/Listicles/"/>
   <updated>2025-06-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/23/Listicles</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;#1. They offer a clear promise.&lt;/strong&gt; “10 reasons” or “5 mistakes” are way better than “on writing” or “some thoughts on blogging.” It’s clear what to expect from the first two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. They are easy to digest and skim.&lt;/strong&gt; People don’t read online. They skim. A listicle with bolded bullet points or subheaders is easier to skim than text-heavy essays. People can jump in at any point after skimming. And even if they only read those subheaders, they will take away some value from a listicle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. They follow a proven structure.&lt;/strong&gt; Listicles are popular because they work. The 10 Commandments, The 48 Laws of Power, and 21 Lessons for the 21st Century are just three examples of listicles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. They offer more interaction points.&lt;/strong&gt; Every point invites comments and conversation. &lt;em&gt;“I really like point #n…“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. They offer more repurposing opportunities.&lt;/strong&gt; Listicle points with the most interaction make great standalone posts. See #4. That’s one tip to &lt;a href=&quot;/2025/01/28/DoubleYourPostCount/&quot;&gt;double our post count&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Best of all? If you already &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;write 10 ideas a day&lt;/a&gt;, listicles practically write themselves.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;ve Replaced My Second Brain With a Simpler Method</title>
   <link href="https://canro91.github.io/2025/06/22/SimplerNoteTaking/"/>
   <updated>2025-06-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/22/SimplerNoteTaking</id>
   <content type="html">&lt;p&gt;For a long time, I was a note-taking freak.&lt;/p&gt;

&lt;p&gt;I spent hours trying to find the perfect note-taking system. I tried a bullet journal, a simple A-4 sheet of paper folded twice, and &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;an endless .txt file&lt;/a&gt;. I probably have an old comparison table of note-taking buried in my old computer.&lt;/p&gt;

&lt;h2 id=&quot;starting-a-second-brain&quot;&gt;Starting a second brain&lt;/h2&gt;

&lt;p&gt;Years later, probably scrolling on Hacker News, I found out about the Zettelkasten method.&lt;/p&gt;

&lt;p&gt;It fascinated me so much that I read &lt;a href=&quot;/2020/11/18/HowToTakeSmartNotes/&quot;&gt;How to Take Smart Notes&lt;/a&gt; to learn more about the method and started my own “zettel.” But recently I realized that I barely revisit most of my notes. I’m not interested in the same topics anymore.&lt;/p&gt;

&lt;h2 id=&quot;simply-writing-10-ideas&quot;&gt;Simply writing 10 ideas&lt;/h2&gt;

&lt;p&gt;And after reading some of &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher’s books&lt;/a&gt; and &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;recovering from burnout&lt;/a&gt;, I’ve changed my mind about productivity and to-do lists.&lt;/p&gt;

&lt;p&gt;Instead of to-do lists and goals, I’ve started to write 10 ideas a day to &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;become an idea machine&lt;/a&gt;. It has changed my note-taking habits too. Now, after every book, podcast, or course, I write down 10 key lessons. It forces me to recall the main ideas from the content I consume. I don’t need to write every single detail but rather remember the 10 points that resonate the most. That effort makes the learning stick.&lt;/p&gt;

&lt;p&gt;I still write down the quotes I like the most from books I read. But I’ve stopped chasing second-brain systems and testing every new AI-powered app. I simply write 10 ideas. It’s simple and more useful than any system I’ve tried.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Writing 10-idea lists is Idea #5 in &lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ignore These 15 Ideas and Get Ghosted When Cold Emailing Clients or Busy People</title>
   <link href="https://canro91.github.io/2025/06/21/ColdEmail/"/>
   <updated>2025-06-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/21/ColdEmail</id>
   <content type="html">&lt;p&gt;In 2019, I started my first attempt at running a freelancing business.&lt;/p&gt;

&lt;p&gt;I had started freelancing for a company I had just left. I thought I could do the same for other companies. My strategy for finding clients? Cold emailing any software agency I could find.&lt;/p&gt;

&lt;p&gt;I even emailed every recruiter I found on open applications on LinkedIn. Those days, LinkedIn was flooded with “life-changing opportunities.” Sorry, I meant job listings.&lt;/p&gt;

&lt;p&gt;I don’t remember how many emails I sent. But I remember getting just one reply. When I followed up, I only heard crickets.&lt;/p&gt;

&lt;h2 id=&quot;here-are-15-ideas-to-reduce-chances-of-getting-ghosted-when-cold-emailing-clients-or-anyone-else&quot;&gt;Here are 15 ideas to reduce chances of getting ghosted when cold emailing clients or anyone else:&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. Send 10 ideas for free.&lt;/strong&gt; Don’t expect anything in return. This is James Altucher’s idea to &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;become an idea machine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Don’t give your reader homework.&lt;/strong&gt; &lt;em&gt;“Fill out this application form to apply.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Make an offer simple to say yes.&lt;/strong&gt; &lt;em&gt;“For $x, I do XYZ.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Don’t make your reader look or feel bad.&lt;/strong&gt; &lt;em&gt;“Your YouTube video thumbnails suck, but I’m here to rescue you.”&lt;/em&gt; Good luck with that!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Show real interest.&lt;/strong&gt; &lt;em&gt;“Hey, I’ve been following your content and liked your take on XYZ”&lt;/em&gt; or something like that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Start with a genuine compliment.&lt;/strong&gt; Probably an idea I picked from “How to Win Friends and Influence People.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Sound like a real human being.&lt;/strong&gt; Write using your own words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Don’t copy pitches from the Internet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Don’t use classical sales tactics.&lt;/strong&gt; &lt;em&gt;“Hurry up! Only X seats left. Limited offer.”&lt;/em&gt; Think you’re reaching out to &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;help, not to close&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Don’t just ask for something in exchange for nothing.&lt;/strong&gt; &lt;em&gt;“Let’s do a collaboration. You do something for me, but I don’t do anything for you.”&lt;/em&gt; Yeah, I’ve received DMs on LinkedIn in those lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#11. Interact with your reader’s content first on social media.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#12. Use a catchy subject line in your email.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#13. Spell your reader’s name or title correctly.&lt;/strong&gt; You don’t know how many emails and DMs I’ve ignored simply because they started with “Hello, coach Cesar.” I don’t have a coaching business and I don’t have “coach” next to my name anywhere online. Somebody just use a template, got you!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#14. Tell them how you found their contact details.&lt;/strong&gt; &lt;em&gt;“I found your email on your LinkedIn profile…“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#15. Include your own socials or portfolio as proof you’re real.&lt;/strong&gt; Nothing fancy, you could try adding them under your signature.&lt;/p&gt;

&lt;p&gt;Last year, I found a job listing I liked. They were looking for people on-site in a different country, but it didn’t stop me. I reached out to the CEO sharing 10 ideas to improve their product landing page. My mistake? I misspelled the company name. Maybe that’s why I never got a reply.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Tricky bugs, Google Translate, and AI</title>
   <link href="https://canro91.github.io/2025/06/20/FridayLinks/"/>
   <updated>2025-06-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/20/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey,&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. I don’t know how many times I’ve had to flip the conditions inside an if. Maybe not my trickiest bugs, but still… Anyway, here are &lt;a href=&quot;https://henrikwarne.com/2025/06/15/lessons-from-9-more-years-of-tricky-bugs/&quot;&gt;9 lessons from a coder’s trickiest bugs&lt;/a&gt; (8min).&lt;/p&gt;

&lt;p&gt;#2. &lt;a href=&quot;https://blog.miguelgrinberg.com/post/why-generative-ai-coding-tools-and-agents-do-not-work-for-me&quot;&gt;This coder doesn’t use AI and explains why&lt;/a&gt; (8min). One of his main concerns? Speed.&lt;/p&gt;

&lt;p&gt;#3. &lt;a href=&quot;https://ingrids.space/posts/what-google-translate-can-tell-us-about-vibecoding/&quot;&gt;Google Translate hasn’t killed translators and interpreters&lt;/a&gt; (6min). Maybe AI won’t kill coding either. At the end of the day, we as coders are translators too.&lt;/p&gt;

&lt;p&gt;#4. It feels like coders will be AI’s first victims. But &lt;a href=&quot;https://greenbluegray.substack.com/p/now-might-be-the-best-time-to-learn&quot;&gt;this might be the best time to learn software development&lt;/a&gt; (8min). Well, some time ago it was with textbooks, StackOverflow, forums, and instruction manuals.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I’ve been playing with Copilot recently. It’s already saved me hours of repetitive work. And in case you missed it, I’ve written about the tasks Copilot has helped me with &lt;a href=&quot;/2025/05/24/CodingWithAI/&quot;&gt;here&lt;/a&gt; (3min) and &lt;a href=&quot;/2025/06/19/CodingWithAI/&quot;&gt;here&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Check &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt; to access free and premium books and courses to level up your coding skills and grow your software engineering career.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>How Copilot Has Helped Me Code Faster—With These 5 Boring Tasks</title>
   <link href="https://canro91.github.io/2025/06/19/CodingWithAI/"/>
   <updated>2025-06-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/19/CodingWithAI</id>
   <content type="html">&lt;p&gt;This week, I’ve been running a coding experiment.&lt;/p&gt;

&lt;p&gt;Every time I sit to code, I open Copilot (on a browser, not inside my IDE) and look for tasks to delegate.&lt;/p&gt;

&lt;p&gt;Yesterday, while migrating a legacy Visual Basic application, I found myself doing some mundane and repetitive work. So I hired Copilot as my junior coder. And here are the tasks it helped me with:&lt;/p&gt;

&lt;h2 id=&quot;1-populate-an-enum&quot;&gt;#1. Populate an enum.&lt;/h2&gt;

&lt;p&gt;I had a large &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;While&lt;/code&gt; loop full of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;If&lt;/code&gt; statements on the VB side.&lt;/p&gt;

&lt;p&gt;It used magic strings for the comparisons. I gave the original VB code and asked Copilot to populate an existing C# enum with the missing magic strings.&lt;/p&gt;

&lt;h2 id=&quot;2-use-comments-to-emulate-named-parameters&quot;&gt;#2. Use comments to emulate named parameters.&lt;/h2&gt;

&lt;p&gt;A method with dozens of parameters isn’t that weird in a legacy app.&lt;/p&gt;

&lt;p&gt;To make it easier to port, I gave Copilot the signature of a method and a code block that invoked it, and asked it to use comments to document each parameter name.&lt;/p&gt;

&lt;p&gt;Later, I found out VB has named parameters. Arrggg! Anyway, that wasn’t code I was planning to commit. It was just to make it easier to read and port to C#.&lt;/p&gt;

&lt;h2 id=&quot;3-avoid-jumping-between-files&quot;&gt;#3. Avoid jumping between files.&lt;/h2&gt;

&lt;p&gt;Why create constants when we could use magic strings, right? Another common mistake on legacy apps.&lt;/p&gt;

&lt;p&gt;In a unit test, in the Arrange part, I needed to generate identical objects, except for the values for a pair of properties.&lt;/p&gt;

&lt;p&gt;The problem? I needed to look at three VB files to come up with the right combination of values for each object. So I gave Copilot the three code blocks and asked it to generate a 3-column table with the values I needed. No more jumping between files.&lt;/p&gt;

&lt;h2 id=&quot;4-convert-stored-procedures-into-entity-framework-corequeries&quot;&gt;#4. Convert stored procedures into Entity Framework Core queries.&lt;/h2&gt;

&lt;p&gt;I’m not going to discuss which one is better. Stored procedures or ORMs.&lt;/p&gt;

&lt;p&gt;But I found myself migrating stored procedures with fairly simple queries to LINQ-like queries with Entity Framework. So I gave Copilot my entity classes and asked to generate the equivalent queries.&lt;/p&gt;

&lt;p&gt;This might not work for large and complex queries inside stored procedures.&lt;/p&gt;

&lt;h2 id=&quot;5-review-a-patch-looking-for-typos&quot;&gt;#5. Review a patch looking for typos.&lt;/h2&gt;

&lt;p&gt;Before opening PRs, I asked Copilot to look for typos and suggest more descriptive class, method, and variable names.&lt;/p&gt;

&lt;p&gt;Naming is hard and it always generates a lot of back and forth in code reviews. To avoid long discussions, I gave Copilot a git diff of my changes and asked it to be my code reviewer.&lt;/p&gt;

&lt;p&gt;I’ve found Copilot shines at text-heavy processing tasks and filling the blanks of well-defined tasks. I’ve already tried &lt;a href=&quot;/2025/05/24/CodingWithAI/&quot;&gt;these five tasks&lt;/a&gt;. But it’s not great at vague instructions like “migrate this to C#” or “refactor this code block.” With clear instructions, it’s the best intern for tedious tasks.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Lessons I Learned from a World-Class Peak Performance Expert</title>
   <link href="https://canro91.github.io/2025/06/18/PeakPerformance/"/>
   <updated>2025-06-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/18/PeakPerformance</id>
   <content type="html">&lt;p&gt;What’s the first thing that comes to mind when you hear “productivity?”&lt;/p&gt;

&lt;p&gt;Eisenhower matrix? Eat that frog? Pomodoro technique? Listing daily and weekly priorities?&lt;/p&gt;

&lt;p&gt;We often mistake productivity for choosing the right tools and techniques. And we procrastinate by tweaking tools instead of doing actual work.&lt;/p&gt;

&lt;p&gt;I was one of those people. Multiple to-do lists, constantly syncing them between devices. But since last year, &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;I’ve ditched my to-do and goal lists&lt;/a&gt;. Instead I’ve been focusing on my energy levels and working on my mind, body, and spirit every day.&lt;/p&gt;

&lt;p&gt;Yesterday, I stumbled upon a webinar by &lt;a href=&quot;https://www.selfmastered.com/&quot;&gt;Leon Castillo&lt;/a&gt;, “an entrepreneur, investor, and university professor obsessed with peak performance &amp;amp; entrepreneurship.” He helped me confirm some of my ideas and made me realize what I was doing wrong.&lt;/p&gt;

&lt;p&gt;Here are 5 lessons I learned from him:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. We aren’t wired for focus.&lt;/strong&gt; I guess our primitive brain developed to look for  small clues in our environment. A slight movement behind a bush could mean we were a fierce predator’s dinner. So we need to create the right conditions for focus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Reduce the amount of information you consume.&lt;/strong&gt; Everything and anything around you competes for the same reserves of attention and energy. Use them wisely. For example, don’t use your cellphone first thing in the morning. Instead use it at lunch or at night if you’re a morning person. Take the &lt;a href=&quot;https://qxmd.com/calculate/calculator_829/morningness-eveningness-questionnaire-meq&quot;&gt;Morningness-Eveningness Questionnaire (MEQ)&lt;/a&gt; to find out if you’re a morning person or a night owl. “Moderate morning” person around here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Have a clean and organized environment for work.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Work next to a window or close to nature.&lt;/li&gt;
  &lt;li&gt;Make your workstation as bright as possible.&lt;/li&gt;
  &lt;li&gt;Don’t rely too much on white noise. Work in silence.&lt;/li&gt;
  &lt;li&gt;Put your phone away. &lt;a href=&quot;/2024/12/13/KeepingPhonesAround/&quot;&gt;It distracts you even when silent or powered off&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;#4. Batch focused work on 90-minute sessions, followed by 20-minute rest sessions.&lt;/strong&gt; Our brain works best in periods of high intensity, followed by periods of low intensity. And the best type of rest activities? Meditation or napping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Meditation doesn’t have to be complicated.&lt;/strong&gt; Sit in silence for 15 minutes, with as little sensory input as possible, and notice your breathing.&lt;/p&gt;

&lt;p&gt;After watching that recording, I felt the urge to audit my daily routines. I need to protect my mornings. No more social media until afternoon.&lt;/p&gt;

&lt;p&gt;And if I could summarize his talk in one single line, it would be: It’s not how hard you push, it’s how well you rest.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>10 Reasons Why I Write 10 Bad Ideas Every Day</title>
   <link href="https://canro91.github.io/2025/06/17/TenIdeas/"/>
   <updated>2025-06-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/17/TenIdeas</id>
   <content type="html">&lt;p&gt;Since October 2024, I’ve been writing 10 bad ideas every day.&lt;/p&gt;

&lt;p&gt;OK, maybe I’ve missed a day or two. But when that happens I write another 10-idea list to keep up.&lt;/p&gt;

&lt;p&gt;I found this habit by pure accident. I was looking for ways to improve my writing skills. And I found “write 10 headlines a day.” That was inspired by James Altucher’s concept of &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;becoming an idea machine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since then, I’ve written 10 bad ideas for books I could write, courses I could create, and writing experiments to run. The point isn’t to come up with good ideas and execute each one, but to think freely and expand our horizons.&lt;/p&gt;

&lt;p&gt;That’s one of the habits I’m sticking to, here are 10 reasons why:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. It helps me calm myself down when stressed.&lt;/strong&gt; Writing 10 ideas redirects my brain from &lt;a href=&quot;/2025/04/19/CalmDown/&quot;&gt;a stressful situation&lt;/a&gt; to a higher-priority task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. It helps me generate content ideas for blog posts and LinkedIn posts.&lt;/strong&gt; Often I turn my 10-idea lists into multiple pieces of content I post online. This post started as a 10 idea list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. It helps me decompose a big project into smaller tasks.&lt;/strong&gt; Before starting a big project, list 10 initial ideas. Then pick one of those first 10 ideas and write another 10 to execute on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. It helps me keep my brain sharp.&lt;/strong&gt; It’s exercise for my brain. Alzheimer’s, stay away from me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. It helps me do something creative every day.&lt;/strong&gt; Writing 10 bad ideas a day keeps my possibility and creativity muscles strong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. It helps me avoid ruminating thoughts.&lt;/strong&gt; Again, it shifts my focus from &lt;a href=&quot;/2024/12/19/TimeTravel/&quot;&gt;reliving the past&lt;/a&gt; to a more productive task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. It helps me train my focus.&lt;/strong&gt; I try not to think about anything else until I write 10 ideas. But there are thousands of thoughts coming and going. The goal is to focus on hitting 10 ideas. And that’s hard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. It replaces note taking.&lt;/strong&gt; After finishing a podcast episode or book, I write a “10 lessons learned” list. It helps me with recalling and retention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. It teaches me to simplify my sentences.&lt;/strong&gt; I use a notepad that fits into my back pocket, so I don’t have much space to write a paragraph. I need to write a short sentence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. It helps me use my cellphone less.&lt;/strong&gt; Life happens away from screens. And since I use old pen and paper to write my ideas, that’s time away from my phone. I started with a DIY notepad made from recycled materials. Then I bought a real notepad.&lt;/p&gt;

&lt;p&gt;I don’t think I’m exaggerating when I say writing 10 ideas a day has made me more creative. Well, without any drama, it has helped me keep my daily writing streak.&lt;/p&gt;

&lt;p&gt;Grab pen and paper and write your first 10 bad ideas. They will push your brain and challenge your creativity. &lt;em&gt;&lt;a href=&quot;/2026/03/04/10SimpleIdeas/&quot;&gt;It changed my life and could change yours too&lt;/a&gt;. Try it!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>There&apos;s Nothing Wrong With Blogging for Attention</title>
   <link href="https://canro91.github.io/2025/06/16/BloggingExpectations/"/>
   <updated>2025-06-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/16/BloggingExpectations</id>
   <content type="html">&lt;p&gt;I’m jumping into the online conversation about blogging expectations.&lt;/p&gt;

&lt;p&gt;I found out about it on &lt;a href=&quot;https://joelchrono.xyz/blog/blogging-expectations/&quot;&gt;joelchrono’s blog&lt;/a&gt;. But somebody else started it. I can’t find those initial posts anymore.&lt;/p&gt;

&lt;h2 id=&quot;heres-what-i-expected-from-my-blog&quot;&gt;Here’s what I expected from my blog&lt;/h2&gt;

&lt;p&gt;I started blogging to improve my coding.&lt;/p&gt;

&lt;p&gt;That’s what Google recommended and I bought the idea. So &lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;in 2018, I started blogging&lt;/a&gt; to document my learning as a coder and the problems I was solving at work.&lt;/p&gt;

&lt;p&gt;A couple of years after my first post, I added analytics with &lt;a href=&quot;https://www.goatcounter.com/&quot;&gt;GoatCounter&lt;/a&gt;. And to my surprise, some people read my posts. Thanks, Google!&lt;/p&gt;

&lt;p&gt;Then to increase my pageviews, I started to repost on &lt;a href=&quot;https://dev.to/canro91&quot;&gt;dev.to&lt;/a&gt; and to share my posts on &lt;a href=&quot;https://linkedin.com/in/iamcesaraguirre&quot;&gt;LinkedIn&lt;/a&gt;. That was the strategy I found to increase my blog traffic.&lt;/p&gt;

&lt;p&gt;So I was writing:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;To document my journey,&lt;/li&gt;
  &lt;li&gt;To please the SEO gods,&lt;/li&gt;
  &lt;li&gt;To rank on Google first page, and&lt;/li&gt;
  &lt;li&gt;To have people reading my posts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started with no plans, just to improve my coding skills. But I discovered SEO and &lt;a href=&quot;/tags/writing/&quot;&gt;a love for writing&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;blogging-has-given-me-a-few-moments-of-attention&quot;&gt;Blogging has given me a few moments of attention&lt;/h2&gt;

&lt;p&gt;Since then, &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;my blog has done more for my career than a portfolio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Apart from occasional virality and a bit of lunch money, my blog led to unexpected results:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Getting my blog curated on &lt;a href=&quot;/2025/03/27/IndieWebAndMinifeed/&quot;&gt;Minifeed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;People on Reddit roasting one of my posts&lt;/li&gt;
  &lt;li&gt;A reader compiling his favorite posts from my blog&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/09/16/LessonsFromHerbertLuiBlog/&quot;&gt;One of my blogging heroes&lt;/a&gt; reacting to one of my posts&lt;/li&gt;
  &lt;li&gt;An interview going faster after I showed &lt;a href=&quot;/UnitTesting&quot;&gt;my unit testing posts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/24/AVeteranOnAI/&quot;&gt;One veteran coder emailing me&lt;/a&gt; after one of my posts going viral&lt;/li&gt;
  &lt;li&gt;Readers emailing me to point typos or issues in my code samples&lt;/li&gt;
  &lt;li&gt;One reader emailing me to share how one of my posts resonated with them&lt;/li&gt;
  &lt;li&gt;Another writing hero tagging me in one of his posts after a discussion in the comments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those unexpected moments showed me that my writing resonated with people. Sometimes I’m not writing into the void.&lt;/p&gt;

&lt;p&gt;Even if &lt;a href=&quot;/2025/06/10/StartingFromZero/&quot;&gt;I had to start over&lt;/a&gt;, I’d still blog. For &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;my mental health and creativity&lt;/a&gt;. And yes, for a bit of attention.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Brutal Truths About Writing I Learned After Posting Over 300 Times on LinkedIn</title>
   <link href="https://canro91.github.io/2025/06/15/LinkedIn/"/>
   <updated>2025-06-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/15/LinkedIn</id>
   <content type="html">&lt;p&gt;I made my first $100 online on LinkedIn.&lt;/p&gt;

&lt;p&gt;It was pure luck. Or a happy accident. Dunno. I wanted people to notice my coding blog and I started to share links to my posts on LinkedIn, pretending to steal their users.&lt;/p&gt;

&lt;p&gt;Of course, it was the wrong strategy. I got almost zero engagement on LinkedIn and almost zero visits to my blog. Social media platforms want their users trapped in the platform.&lt;/p&gt;

&lt;p&gt;But by sharing those blog posts, the head of marketing of a software company reached out and asked me to write something similar for them. Boom! My first $100.&lt;/p&gt;

&lt;p&gt;At some point, I gave up posting and abandoned my LinkedIn account.&lt;/p&gt;

&lt;p&gt;But those $100 were in the back of my head. In 2024, I challenged myself to write 100 native posts on LinkedIn to revive my account and see what happened.&lt;/p&gt;

&lt;p&gt;Since then, I’ve published over 300 posts. This is what I learned:&lt;/p&gt;

&lt;h2 id=&quot;1-theres-no-a-single-skill-called-writing&quot;&gt;1. There’s no a single skill called “writing”&lt;/h2&gt;

&lt;p&gt;Writing isn’t just one skill.&lt;/p&gt;

&lt;p&gt;It’s a collection of subskills:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Writing hooks,&lt;/li&gt;
  &lt;li&gt;Formatting and editing,&lt;/li&gt;
  &lt;li&gt;Storytelling,&lt;/li&gt;
  &lt;li&gt;Copywriting,&lt;/li&gt;
  &lt;li&gt;Offer creation…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You have to practice and master each one.&lt;/p&gt;

&lt;h2 id=&quot;2-writing-can-feel-lonely&quot;&gt;2. Writing can feel lonely&lt;/h2&gt;

&lt;p&gt;We all start from almost zero, apart from ex-coworkers and friends as connections.&lt;/p&gt;

&lt;p&gt;In my first days, I published my posts and sat down waiting for people to come and like and comment my posts… and waited… and waited… and waited. It was a small town in the Wild West before a Mexican standoff.&lt;/p&gt;

&lt;p&gt;Make friends and interact with other accounts. Otherwise, it’ll be you and your screen alone.&lt;/p&gt;

&lt;h2 id=&quot;3-writing-is-an-infinite-game&quot;&gt;3. Writing is an infinite game&lt;/h2&gt;

&lt;p&gt;And like any other infinite game, you lose if you stop playing.&lt;/p&gt;

&lt;p&gt;So keep writing. Write as if nobody is watching. Write for your past self. And keep writing because you don’t know who you’re helping.&lt;/p&gt;

&lt;h2 id=&quot;4-an-intention-makes-you-start-but-a-system-keeps-you-showing-up&quot;&gt;4. An intention makes you start but a system keeps you showing up&lt;/h2&gt;

&lt;p&gt;Find ways to capture ideas and the content you consume and turn them into content.&lt;/p&gt;

&lt;p&gt;I keep an eye on everything I consume: books, podcasts, and social media posts. I capture and connect those ideas, and then I write about them.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;I write 10 bad ideas a day&lt;/a&gt;. Credits to James Altucher. Most of those 10 ideas come from the content I consume. In fact, this post started as a 10-idea list. And I stole this subject from a creator I follow.&lt;/p&gt;

&lt;p&gt;When you look with the right attitude, &lt;a href=&quot;/2025/01/26/ContentIsEverywhere/&quot;&gt;there’s content everywhere&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;5-you-cant-predict-your-next-hit&quot;&gt;5. You can’t predict your next hit&lt;/h2&gt;

&lt;p&gt;Often, my most viewed posts are the ones I write to mark the calendar and call it a day. Not the ones where I spend time choosing the right image or crafting the best post body.&lt;/p&gt;

&lt;p&gt;Since you can’t predict your next hit, keep posting, look at the data, and iterate.&lt;/p&gt;

&lt;h2 id=&quot;6-hooks-can-make-a-huge-difference&quot;&gt;6. Hooks can make a huge difference&lt;/h2&gt;

&lt;p&gt;Picture this, someone busy or bored at work is scrolling down their LinkedIn feed, waiting for the next dopamine hit.&lt;/p&gt;

&lt;p&gt;You have to make them stop scrolling with your posts. For that, you need an attention-grabbing first line. That’s what in LinkedIn terms is called a “hook.” It’s your welcome sign.&lt;/p&gt;

&lt;p&gt;Often I just repost the same post, but with a better hook and, boom! I get way better engagement.&lt;/p&gt;

&lt;p&gt;A good hook can make a huge difference.&lt;/p&gt;

&lt;h2 id=&quot;7-if-it-can-benefit-one-person-you-can-write-about-it&quot;&gt;7. If it can benefit one person, you can write about it&lt;/h2&gt;

&lt;p&gt;When I started writing, it was hard to decide what to write about.&lt;/p&gt;

&lt;p&gt;You’re throwing everything at the wall to see what sticks. But if what you’re writing can benefit one person, you can write about stories, lessons, mistakes, rants, challenges… Anything.&lt;/p&gt;

&lt;p&gt;Remember you’re writing to serve your readers, not to show how smart you are.&lt;/p&gt;

&lt;h2 id=&quot;8-writing-is-the-front-door-for-sales&quot;&gt;8. Writing is the front door for sales&lt;/h2&gt;

&lt;p&gt;Build an audience and serve that audience and you’ll make money. I’m still figuring this out. I’ll share results in the future.&lt;/p&gt;

&lt;h2 id=&quot;9-your-best-writing-course-is-noticing-your-behavior-as-reader&quot;&gt;9. Your best writing course is noticing your behavior as reader&lt;/h2&gt;

&lt;p&gt;If you want to get better at writing, stop being a dopamine junkie and put on your creator glasses.&lt;/p&gt;

&lt;p&gt;Every time you stop to read something, ask yourself:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Why did this post make me stop?&lt;/li&gt;
  &lt;li&gt;What type of visuals did it have?&lt;/li&gt;
  &lt;li&gt;What structure did it use?&lt;/li&gt;
  &lt;li&gt;What hook did it use?&lt;/li&gt;
  &lt;li&gt;What call-to-action?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then replicate that in your content. Steal like an artist.&lt;/p&gt;

&lt;h2 id=&quot;10-dont-give-up-on-an-idea-unless-you-try-it-multiple-times&quot;&gt;10. Don’t give up on an idea unless you try it multiple times&lt;/h2&gt;

&lt;p&gt;You can’t predict your hits. Remember? And if a post has low engagement, it doesn’t mean it was a bad idea.&lt;/p&gt;

&lt;p&gt;Give it another lick of paint and try it again. Don’t worry there’s nothing wrong with repurposing old content. Nobody will notice. And everybody does it.&lt;/p&gt;

&lt;p&gt;Writing on LinkedIn (or anywhere online) makes people see you as an expert. Maybe you’ll land your next job from posting. You’ll make new professional connections (your network is your net worth). Or simply you’ll make $100. I can promise anything, but why not trying it yourself?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Real #1 Sign You&apos;ve Become a Senior Software Engineer</title>
   <link href="https://canro91.github.io/2025/06/14/Senior/"/>
   <updated>2025-06-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/14/Senior</id>
   <content type="html">&lt;p&gt;I’m not exactly sure when “Senior” made its way into my title.&lt;/p&gt;

&lt;p&gt;In my last full-time job, I was “Software Engineer 1” out of 5. But I worked on the main features of the software, was in the on-call rotation, and sat in meetings with the company’s president. A “SWE 1” wasn’t supposed to be doing most of those tasks.&lt;/p&gt;

&lt;p&gt;OK, that was at a small shop in my city, so maybe it wasn’t that impressive. And it took me about 5 years and &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;a lot of hard work&lt;/a&gt; to get there.&lt;/p&gt;

&lt;p&gt;Although “Senior” wasn’t officially part of my title, there was &lt;a href=&quot;/2022/12/18/LessonsFromExCoworkers/&quot;&gt;a sign I saw in my coworkers and mentors&lt;/a&gt;, and later connecting the dots, I noticed I had absorbed from them:&lt;/p&gt;

&lt;p&gt;Knowing when to stop because a solution is good enough.&lt;/p&gt;

&lt;p&gt;As non-seniors, we keep wasting time:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Adding abstractions,&lt;/li&gt;
  &lt;li&gt;Micro-optimizing infrequently-used code,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;Debating variable names during code reviews&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;Starting &lt;a href=&quot;/2023/09/04/AgainstMassiveUnrequestedRefactorings/&quot;&gt;massive unrequested refactorings&lt;/a&gt;, and&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/05/17/NeedForScale/&quot;&gt;Overengineering our app to make it scale&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re senior when you truly master YAGNI, not only in your code files, but across your entire career.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Friday Links: Layoffs, outliers, and interviewing</title>
   <link href="https://canro91.github.io/2025/06/13/FridayLinks/"/>
   <updated>2025-06-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/13/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. The day I “was let go” last year, I was told it was because of how tough the economy was doing. High interest rates? AI? But this news article shows &lt;a href=&quot;https://qz.com/tech-layoffs-tax-code-trump-section-174-microsoft-meta-1851783502&quot;&gt;the real reason behind layoffs in the tech sector&lt;/a&gt; (10min). Spoiler alert: nothing to do with AI.&lt;/p&gt;

&lt;p&gt;#2. With AI, &lt;a href=&quot;https://quic.video/blog/be-the-outlier&quot;&gt;our job is to know when to introduce outliers&lt;/a&gt; (4min). “In a sea of fish, you want to be a narwhal.”&lt;/p&gt;

&lt;p&gt;#3. Like coding, interviewing is another skill to master. Yes, I agree the tech interview is broken and we don’t know how to fix it. But if you’re actively interviewing, here are &lt;a href=&quot;https://www.viblo.se/posts/interviewing/&quot;&gt;8 dos and don’ts to ace your next interviews&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#4. At some point we all as coders have to make a choice: stay technical or jump the management track. The thing is &lt;a href=&quot;https://www.blog4ems.com/p/being-an-engineering-manager-today-has-never-been-harder&quot;&gt;being an engineering manager has never been harder&lt;/a&gt; (5min), too many hats to wear.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/06/10/StartingFromZero/&quot;&gt;10 steps I’d take to build a profitable brand from scratch&lt;/a&gt; (4min) and &lt;a href=&quot;/2025/06/12/WorstMoments/&quot;&gt;10 lessons I learned from the worst moment of my career&lt;/a&gt; (4min). Not much about coding this week.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices to write readable and maintainable unit tests in C# while refactoring real unit tests from my past projects.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>10 Life-Changing Lessons I Learned the Hard Way—from the Worst Moment of My Career</title>
   <link href="https://canro91.github.io/2025/06/12/WorstMoments/"/>
   <updated>2025-06-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/12/WorstMoments</id>
   <content type="html">&lt;p&gt;Two years ago, my life sucked. Completely. I felt like a complete loser.&lt;/p&gt;

&lt;p&gt;In 2023, I got sick. One day, out of the blue, I started to rush to the bathroom after every meal. And it wasn’t to throw up.&lt;/p&gt;

&lt;p&gt;By the end of 2023, I burned out. And in 2024, I was laid off. A simple call over Microsoft Teams ended my 5-year career at a company.&lt;/p&gt;

&lt;p&gt;I felt so lost. I felt more lost than when I was a teenager out of high school trying to figure out life as an adult.&lt;/p&gt;

&lt;p&gt;That was the worst moment of my career and probably of my life. But that whole situation taught me these 10 lessons:&lt;/p&gt;

&lt;h2 id=&quot;1-your-health-and-well-being-are-more-important-than-any-job&quot;&gt;1. Your health and well-being are more important than any job.&lt;/h2&gt;

&lt;p&gt;When I burned out, I had stopped working out, running, and eating healthy.&lt;/p&gt;

&lt;p&gt;I was so focused on my career that I had forgotten about resting and taking care of my health. It was a painful mistake.&lt;/p&gt;

&lt;p&gt;Remember you can always get a new job and a new career, but not a new body.&lt;/p&gt;

&lt;h2 id=&quot;2-youre-more-than-your-job-title&quot;&gt;2. You’re more than your job title.&lt;/h2&gt;

&lt;p&gt;For years, my career was probably the most important part of my life.&lt;/p&gt;

&lt;p&gt;My job was what brought me a sense of accomplishment, fulfillment, and joy… until I heard a “We have to let you go” on a call. It shook my world because I had wrapped my entire identity around a title: “Software Engineer.” It became all I thought I was.&lt;/p&gt;

&lt;p&gt;Remember, &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;diversify your sources of joy and fulfillment&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-build-multiple-sources-of-income-like-your-life-depends-on-it&quot;&gt;3. Build multiple sources of income like your life depends on it.&lt;/h2&gt;

&lt;p&gt;No job is safe.&lt;/p&gt;

&lt;p&gt;I used to think being an employee was the safest route. I don’t know who made me believe that. I was so wrong. Being an employee is like having one single customer who can decide to stop buying from you at any time.&lt;/p&gt;

&lt;p&gt;Don’t only rely on your paycheck. Have more than one way to make money.&lt;/p&gt;

&lt;h2 id=&quot;4-the-moment-you-stop-learning-and-growing-its-time-to-go&quot;&gt;4. The moment you stop learning and growing, it’s time to go.&lt;/h2&gt;

&lt;p&gt;For months and years, I delayed the decision of finding another job or starting my own thing. Being at a “good enough” job was the most expensive decision for my career.&lt;/p&gt;

&lt;h2 id=&quot;5-do-something-that-brings-you-joy-every-day&quot;&gt;5. Do something that brings you joy every day.&lt;/h2&gt;

&lt;p&gt;It took me months, probably one entire year, to &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;feel free from burnout&lt;/a&gt;. The path was simple but slow: going back to my hobbies and practicing them every day.&lt;/p&gt;

&lt;h2 id=&quot;6-your-connections-and-online-presence-are-way-better-than-a-cv&quot;&gt;6. Your connections and online presence are way better than a CV.&lt;/h2&gt;

&lt;p&gt;Right after the layoff, I felt relieved. No more meetings or emails.&lt;/p&gt;

&lt;p&gt;But after a few weeks, I went into panic mode. I realized no paycheck was coming. I applied to anything with “software engineer” in the job description. &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;I took the CV route&lt;/a&gt;. And I don’t have to say it led me nowhere.&lt;/p&gt;

&lt;p&gt;When &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;the layoff season came&lt;/a&gt;, I didn’t have a solid network and I had set aside my online presence. Your brand is your CV and portfolio.&lt;/p&gt;

&lt;h2 id=&quot;7-be-careful-with-what-you-put-in-your-body-and-mind&quot;&gt;7. Be careful with what you put in your body and mind.&lt;/h2&gt;

&lt;p&gt;Apart from pills, to recover from my stomach sickness, I had to eat fruits and vegetables, at a fixed schedule, and eat them slowly.&lt;/p&gt;

&lt;p&gt;To recover from burnout? It started with an information diet.&lt;/p&gt;

&lt;p&gt;I cut news, music, podcasts, TV shows, movies…. I only focused on binge-watching Borja Vilaseca, a Spanish YouTuber with an inspiring story of personal and professional reinvention.&lt;/p&gt;

&lt;h2 id=&quot;8-change-and-reinvention-start-in-your-mind&quot;&gt;8. Change and reinvention start in your mind.&lt;/h2&gt;

&lt;p&gt;It was a little voice in my head that made me start again.&lt;/p&gt;

&lt;p&gt;“If you don’t get up by yourself, nobody else will do it for you.” Maybe it was all the inspiring YouTube videos and books I had started to consume.&lt;/p&gt;

&lt;p&gt;You have to choose yourself first. Always.&lt;/p&gt;

&lt;h2 id=&quot;9-listen-to-your-body-for-small-clues&quot;&gt;9. Listen to your body for small clues.&lt;/h2&gt;

&lt;p&gt;I didn’t wake up burned out. It was a slow process on the way down.&lt;/p&gt;

&lt;p&gt;Now that I connect the dots, there was a clear sign I failed to notice: not wanting to get out of bed. My body was yelling and I ignored it.&lt;/p&gt;

&lt;h2 id=&quot;10-your-current-struggles-will-become-lessons-and-stories-to-share&quot;&gt;10. Your current struggles will become lessons and stories to share.&lt;/h2&gt;

&lt;p&gt;After more than one year, I could say I’m free from burnout.&lt;/p&gt;

&lt;p&gt;But just thinking of where I was makes me anxious again. That’s my best motivation to keep working today. I thought I wouldn’t make it. Being healthy and doing something I love felt like a distant goal I would never reach.&lt;/p&gt;

&lt;p&gt;No storm lasts forever. Your current struggles will become lessons and stories to share. Like the French say: “Après la pluie, le beau temps.” There’s always sunshine after the rain.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: You Don&apos;t Need AsNoTracking() When Projecting Entities With Entity Framework Core</title>
   <link href="https://canro91.github.io/2025/06/11/Tracking/"/>
   <updated>2025-06-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/11/Tracking</id>
   <content type="html">&lt;p&gt;Every time we retrieve an entity with Entity Framework Core, it will track those entities in its change tracker. And when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.SaveChanges()&lt;/code&gt;, those changes are persisted to the database.&lt;/p&gt;

&lt;p&gt;For read-only queries, we can add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.AsNoTracking()&lt;/code&gt; to make them faster.&lt;/p&gt;

&lt;p&gt;But when projecting an entity into a custom object, there’s no need to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsNoTracking()&lt;/code&gt; since Entity Framework doesn’t track query results with a type different from the underlying entity type. &lt;a href=&quot;https://learn.microsoft.com/en-us/ef/core/querying/tracking#tracking-and-custom-projections&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, let’s save some movies and retrieve them with and without a custom projection,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LookMaEntityFrameworkDoesNotTrackProjections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MovieTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EFDoesNotTrackProjections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContextOptionsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseInMemoryDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;databaseName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MovieDB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 0. Saving two movies&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Matrix&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1999&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 1. Using a custom projection&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstMovieNameAndReleaseYear&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1990&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// This is a custom projection&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;noTracking&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;noTracking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//             ^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// No entities tracked&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 2. Using AsNoTracking&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstMovieWithNoTracking&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1990&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AsNoTracking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;withAsNoTracking&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;withAsNoTracking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//             ^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// As imply by its name, no entities tracked here&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 3. Retrieving a Movie&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstMovie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1990&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beingTracked&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChangeTracker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beingTracked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//             ^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Since we&apos;re retrieving only one Movie, tracking happens here&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Only when we queried the first movie without a projection and without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.AsNoTracking()&lt;/code&gt;, Entity Framework Core tracked the underlying entity.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;For more tricks with Entity Framework Core, read &lt;a href=&quot;/2025/01/22/EFAndDefaultConstraints/&quot;&gt;how to configure default values for nullable columns with default constraints&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Steps I&apos;d Take to Build a Profitable Online Brand from Zero</title>
   <link href="https://canro91.github.io/2025/06/10/StartingFromZero/"/>
   <updated>2025-06-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/10/StartingFromZero</id>
   <content type="html">&lt;p&gt;In 2018, I wrote my first words online.&lt;/p&gt;

&lt;p&gt;I had no idea what to do and how to do it. I just Googled how to get better at coding and found “start a blog.”&lt;/p&gt;

&lt;p&gt;After procrastinating for a couple of days, choosing a name and a theme for my blog, I dumped a bunch of words into a file and hit Publish.&lt;/p&gt;

&lt;p&gt;After years of trial and error, my analytics grew from 10 visitors a month to 1,000 visitors and I made my first $100 by pure luck.&lt;/p&gt;

&lt;p&gt;But if I had to start all over again, here are 10 actions I’d take:&lt;/p&gt;

&lt;h2 id=&quot;1think-of-my-online-presence-as-a-business&quot;&gt;1. Think of my online presence as a business&lt;/h2&gt;

&lt;p&gt;Writing online was just something cool I did on the side of my 9-to-5.&lt;/p&gt;

&lt;p&gt;For so long, I only thought my online presence would make me appear “attractive” to recruiters and hiring managers. I didn’t know our online presence is our most valuable asset.&lt;/p&gt;

&lt;p&gt;Our personal brand is our own one-person business.&lt;/p&gt;

&lt;h2 id=&quot;2start-on-a-social-blog-like-medium-or-quora&quot;&gt;2. Start on a social blog like Medium or Quora&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;I started a blog&lt;/a&gt; because it was the only free advice I found.&lt;/p&gt;

&lt;p&gt;But the thing with blogs is that our content is at the mercy of search engines, SEO, and their bots. Social blogs have a “feed” and algorithm to match readers with content. And that’s the best way to get faster feedback as a writer.&lt;/p&gt;

&lt;p&gt;As a coder, I’d start in a social blog like dev.to or Medium, sharing &lt;a href=&quot;/2025/04/10/TilPosts/&quot;&gt;TIL posts&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3use-a-social-platform-like-x-or-linkedin&quot;&gt;3. Use a social platform like X or LinkedIn&lt;/h2&gt;

&lt;p&gt;I only started on LinkedIn 4 or 5 years after starting my blog.&lt;/p&gt;

&lt;p&gt;And I only shared links to my posts, pretending to steal LinkedIn users for my blog. Instead, I’d use X or LinkedIn to test ideas, share nuggets of my long-form content, and grow a newsletter. Speaking of which…&lt;/p&gt;

&lt;h2 id=&quot;4start-a-newsletter-from-day-1&quot;&gt;4. Start a newsletter from day 1&lt;/h2&gt;

&lt;p&gt;Subscribers are the real metric to track. Not followers, likes, or comments.&lt;/p&gt;

&lt;p&gt;They are our true fans. From day 1, I’d invite readers to &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;join my newsletter&lt;/a&gt; at the end of every post and I’d email them weekly.&lt;/p&gt;

&lt;h2 id=&quot;5use-consistent-user-handles&quot;&gt;5. Use consistent user handles&lt;/h2&gt;

&lt;p&gt;Oh boy! I used to think hacker-like user handles, like Napster, Codemaestro, or Neo were cool.&lt;/p&gt;

&lt;p&gt;Instead I’d use a more professional user handle across all platforms.&lt;/p&gt;

&lt;h2 id=&quot;6go-for-volume-first&quot;&gt;6. Go for volume first&lt;/h2&gt;

&lt;p&gt;I only wrote on my blog when I thought I had something to say.&lt;/p&gt;

&lt;p&gt;That was once in a blue moon. And when &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;I started on LinkedIn&lt;/a&gt;, that was once a week. It’s no surprise I only saw traction when I went for volume.&lt;/p&gt;

&lt;p&gt;If I were to start again, I’d &lt;a href=&quot;/2025/03/17/VolumeWins/&quot;&gt;go with volume&lt;/a&gt;: 30 long-form pieces and at least 100 short-form posts to get the juices flowing.&lt;/p&gt;

&lt;h2 id=&quot;7come-up-with-a-simple-content-plan&quot;&gt;7. Come up with a simple content plan&lt;/h2&gt;

&lt;p&gt;For so long, I only wrote when I had something to say. No content plan.&lt;/p&gt;

&lt;p&gt;But to fix that, I’d send one or two long-form emails to my newsletter, and repurpose them into one daily short-form post every week. That’s &lt;a href=&quot;/2025/06/01/DanKoe/&quot;&gt;the simple content plan from millionaire creators&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;8find-ways-to-monetize-earlier-coursesebookstemplates&quot;&gt;8. Find ways to monetize earlier: courses/ebooks/templates&lt;/h2&gt;

&lt;p&gt;Making money from my online presence was an afterthought. I didn’t even know I could make money online.&lt;/p&gt;

&lt;p&gt;To start again, I’d come up with monetization strategies after the first or second month. For example, I’d package my best-performing content into an ebook or a course and put a price tag on it somewhere like &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;Gumroad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Those first few dollars would motivate me to keep creating and fund higher-quality products.&lt;/p&gt;

&lt;h2 id=&quot;9only-after-1-8-buy-a-domain-and-start-a-website&quot;&gt;9. Only after #1-#8, buy a domain and start a website&lt;/h2&gt;

&lt;p&gt;I’ve built my presence on rented land. Ooops!&lt;/p&gt;

&lt;p&gt;And when I tried to buy a domain under my name, it was already taken. It turns out there’s a famous soccer player, a singer, and a film director with my same first and last name. Arrggg!&lt;/p&gt;

&lt;p&gt;I’d buy a domain name, even if I didn’t use it. Or to simply point to a landing page linking to my newsletter and my other accounts.&lt;/p&gt;

&lt;h2 id=&quot;10invest-in-courses-training-and-coaching&quot;&gt;10. Invest in courses, training, and coaching&lt;/h2&gt;

&lt;p&gt;Six years passed before I bought &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;my first writing course&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of course, I had consumed a lot of YouTube content to learn about SEO and blog post writing. But those were only two skills I needed to learn to make my online presence a real business.&lt;/p&gt;

&lt;p&gt;I’d invest more in education to save myself a lot of time and take all the money I was leaving on the table. Well, it’s never too late to start again and invest in our online presence and turn it into something real.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What True Wealth Really Looks Like (And It Isn&apos;t Money)</title>
   <link href="https://canro91.github.io/2025/06/09/TrueWealth/"/>
   <updated>2025-06-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/09/TrueWealth</id>
   <content type="html">&lt;p&gt;What does being wealthy truly mean?&lt;/p&gt;

&lt;p&gt;Yesterday, before starting 21 Lessons for the 21st Century, I watched &lt;a href=&quot;https://www.youtube.com/watch?v=6hc6TNV6F-g&quot;&gt;a YouTube interview with Yuval Noah Harari&lt;/a&gt; about that book. That was part of &lt;a href=&quot;/2025/01/09/ReadingStrategy/&quot;&gt;my new reading strategy&lt;/a&gt;. Authors often go on podcast tours to promote their books, sharing their insights and the intentions behind their books.&lt;/p&gt;

&lt;p&gt;Towards the end, the interview closed with this unexpected idea:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“If you’re important, you don’t have a phone. If you work for somebody else, you have a phone”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was shocking but eye-opening. The point isn’t about owning a phone per se but about having full control of our time. A phone means your boss might interrupt you with any apparent urgent issue.&lt;/p&gt;

&lt;p&gt;That line hit really hard. It was only after a layoff that I realized I felt the pressure of always being available and responding to messages. &lt;a href=&quot;/2024/12/21/MessySalary/&quot;&gt;I didn’t know how a salary had messed with my mind&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;True wealth isn’t measured in currency, but in the ability to own our time. And I need to dump my phone.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Examples That Show How to Write Headlines Readers Can&apos;t Ignore</title>
   <link href="https://canro91.github.io/2025/06/08/SampleHeadlines/"/>
   <updated>2025-06-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/08/SampleHeadlines</id>
   <content type="html">&lt;p&gt;What started as mindless scrolling on Medium yesterday turned into a headline-writing exercise.&lt;/p&gt;

&lt;p&gt;I decided to stop scrolling like a zombie and pay attention to the headlines I found. To practice, I rewrote some headlines for clarity and curiosity. I wanted to follow &lt;a href=&quot;/2025/06/05/IrresistibleHeadlines/&quot;&gt;a top Medium writer’s formula for engaging headlines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are 5 headlines I found, plus my versions:&lt;/p&gt;

&lt;h2 id=&quot;1-rethinking-the-value-of-doing-nothing&quot;&gt;1. “Rethinking the Value of Doing Nothing”&lt;/h2&gt;

&lt;p&gt;This was a short story about a random walk in the park. An old man sitting on the grass made the author reflect on rest and productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My version:&lt;/strong&gt; “An Old Man Sitting in the Park Changed My Mind About Productivity”&lt;/p&gt;

&lt;h2 id=&quot;2-my-experience-with-chatgpt&quot;&gt;2. “My Experience With ChatGPT”&lt;/h2&gt;

&lt;p&gt;My first thought: why should I care? Who are you? What did you use ChatGPT for? This post was from a writer who tried ChatGPT to speed up her writing process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My version:&lt;/strong&gt; “I Hired ChatGPT as My Writing Intern—Here’s What I Learned”&lt;/p&gt;

&lt;h2 id=&quot;3-7-things-that-show-youre-making-progress&quot;&gt;3. “7 Things That Show You’re Making Progress”&lt;/h2&gt;

&lt;p&gt;Making progress in what? A video game? Business? This was a post about small signs of progress when nothing else seems to be working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My version:&lt;/strong&gt; “7 True Signs You’re Making Progress in Life (Even When Nothing Seems to Work)”&lt;/p&gt;

&lt;h2 id=&quot;4-the-beautiful-habit-ive-picked-up-lately&quot;&gt;4. “The Beautiful Habit I’ve Picked Up Lately”&lt;/h2&gt;

&lt;p&gt;This headline is too vague. A habit for what? Health? Business? A habit to achieve what? What makes it beautiful? This was a post about starting meaningful conversations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My version:&lt;/strong&gt; “This Simple Habit Has Taught Me More About Human Connection (Than Any Self-Help Book)”&lt;/p&gt;

&lt;h2 id=&quot;5-7-obvious-signs-top-email-scammers-use-to-contact-you&quot;&gt;5. “7 Obvious Signs Top Email Scammers Use to Contact You”&lt;/h2&gt;

&lt;p&gt;This was a post dissecting an email with the classic fake job listing scam. Its headline could benefit from more drama.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My version:&lt;/strong&gt; “Ignore These 7 Signs and You’ll Fall Prey to Email Scammers Offering You Fake Jobs”&lt;/p&gt;

&lt;p&gt;Clear beats clever. “On writing” or “On the value of consistency” tells us nothing. I declare myself guilty, too. A headline isn’t just a title, it’s a promise. Keep it clear, or lose your readers.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Actions to Help New Online Writers Stay Consistent (Year After Year)</title>
   <link href="https://canro91.github.io/2025/06/07/Consistency/"/>
   <updated>2025-06-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/07/Consistency</id>
   <content type="html">&lt;p&gt;The hardest part of being a new writer isn’t writing, but writing when nobody is reading.&lt;/p&gt;

&lt;p&gt;For me, it took me years before I saw my first 1,000 blog visitors. And when &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;I revived my LinkedIn account&lt;/a&gt; in 2024, it took me over a year for my first post to finally go “viral.”&lt;/p&gt;

&lt;p&gt;Those first months feel like shouting into the void. No likes, no comments, no followers. Nothing.&lt;/p&gt;

&lt;p&gt;But to see results, we have to push through that phase. Here are 5 actions to try to keep showing up in those hard moments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Have a clear goal (or an anti-goal).&lt;/strong&gt; Sometimes knowing what you don’t want is more powerful than knowing what you do want. For most of us, it’s the side gig that keeps us from going back to corporate cubicle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Join a community or find a buddy.&lt;/strong&gt; It’s easier to keep showing up when there’s someone walking the same path next to you. Someone who encourages you and gives genuine feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Develop a habit.&lt;/strong&gt; Find a time and place to practice your craft. And &lt;a href=&quot;/2024/12/14/Consistency/&quot;&gt;make the commitment to show up&lt;/a&gt;, even if that means staring at a blank page. Write or create for yourself, &lt;a href=&quot;/2025/03/17/VolumeWins/&quot;&gt;go for volume&lt;/a&gt;, and focus on improving your craft.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Celebrate every small victory.&lt;/strong&gt; Screenshot every extra like or new follower and revisit them when you feel like breaking the chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Find someone to make proud or jealous. Or to prove wrong.&lt;/strong&gt; From the E-Myth Revisited, entrepreneurs aren’t the ones who start most businesses, but frustrated employees. Maybe to prove to their boss that they could create a better business, or to make an ex jealous. Sometimes, the best motivator is hearing “You can’t do it” and proving them wrong.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: AI taking our jobs, being helpful, and judgment</title>
   <link href="https://canro91.github.io/2025/06/06/FridayLinks/"/>
   <updated>2025-06-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/06/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. Is AI really taking our jobs? Here’s a breakdown of papers and historical records to prove that the &lt;a href=&quot;https://sparktoro.com/blog/ai-will-replace-all-the-jobs-is-just-tech-execs-doing-marketing/&quot;&gt;“AI taking jobs” hype is more a marketing strategy&lt;/a&gt; (12min) than anything else. There’s nothing to worry about (yet?) after all.&lt;/p&gt;

&lt;p&gt;#2. There’s a difference between &lt;a href=&quot;https://betterthanrandom.substack.com/p/if-you-are-useful-it-doesnt-mean&quot;&gt;being helpful and being valued at work&lt;/a&gt; (4min). One of the two leads to stagnation in our careers.&lt;/p&gt;

&lt;p&gt;#3. AI has made it easier to start coding or writing. &lt;a href=&quot;https://notsocommonthoughts.com/blog/ai-and-judgement/&quot;&gt;The real differentiator isn’t technical skills, but judgment&lt;/a&gt; (3min). The real question is knowing what to build.&lt;/p&gt;

&lt;p&gt;#4. Here’s &lt;a href=&quot;https://addyo.substack.com/p/the-prompt-engineering-playbook-for&quot;&gt;a complete guide on prompt engineering&lt;/a&gt; (38min) tailored for software engineers, with good and bad examples of prompts. It looks more like a mini-course. Definitely something to keep returning every time we need to write a prompt for a coding task.&lt;/p&gt;

&lt;p&gt;As a bonus, I found &lt;a href=&quot;https://absurd.website&quot;&gt;this absurd website&lt;/a&gt; with funny and absurd project ideas. I think I could use the Lucky Cat to bring more luck to &lt;a href=&quot;https://imcsarag.gumroad.com/&quot;&gt;my Gumroad store&lt;/a&gt;. That one isn’t so absurd.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/06/03/Hype/&quot;&gt;7 strategies to stay sane in this AI-hype cycle&lt;/a&gt; (3min) and &lt;a href=&quot;/2025/05/31/Nightmares/&quot;&gt;12 nightmares every coder faces&lt;/a&gt; (3min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices to write readable and maintainable unit tests in C#…while refactoring real unit tests from my past projects.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>This Top Medium Writer&apos;s Secret Will Make Your Headlines Impossible to Ignore</title>
   <link href="https://canro91.github.io/2025/06/05/IrresistibleHeadlines/"/>
   <updated>2025-06-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/05/IrresistibleHeadlines</id>
   <content type="html">&lt;p&gt;No headline, no readers. As simple as that.&lt;/p&gt;

&lt;p&gt;A headline is your welcome sign. If you don’t have a good one, readers won’t stop by. And no matter how thoughtful the content is, a weak headline means they won’t click or read past the first line.&lt;/p&gt;

&lt;p&gt;And the best way to learn how to write headlines? Steal from the best. Just like artists do.&lt;/p&gt;

&lt;h2 id=&quot;stealing-some-proven-headline-examples&quot;&gt;Stealing some proven headline examples&lt;/h2&gt;

&lt;p&gt;I had already stolen &lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;headlines from a famous YouTuber&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But this time, I’m stealing &lt;a href=&quot;https://derekhughes1.medium.com/&quot;&gt;Derek Hughes’ headlines&lt;/a&gt;, a top Medium writer with over 20,000 followers.&lt;/p&gt;

&lt;p&gt;Here are the headlines of some of his most liked posts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;5 ways to gain an unfair advantage as a writer (no one tells you about)&lt;/li&gt;
  &lt;li&gt;How to escape the life you have (the simplest habit that changed everything)&lt;/li&gt;
  &lt;li&gt;How to write an article in 5 easy steps (that anyone can follow)&lt;/li&gt;
  &lt;li&gt;This is why you’re losing readers (3 ways to keep them hooked)&lt;/li&gt;
  &lt;li&gt;4 killer openers that will hook your readers in seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those headlines are irresistible. But there’s something behind them.&lt;/p&gt;

&lt;h2 id=&quot;the-secret-formula-behind-irresistible-headlines&quot;&gt;The secret formula behind irresistible headlines&lt;/h2&gt;

&lt;p&gt;Each follows a simple formula:&lt;/p&gt;

&lt;p&gt;Promise + outcome + emotion (curiosity/fear/desire)&lt;/p&gt;

&lt;p&gt;Let’s dissect them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;5 ways to gain an unfair advantage as a writer (no one tells you about)
    &lt;ul&gt;
      &lt;li&gt;Promise: “5 ways”&lt;/li&gt;
      &lt;li&gt;Outcome: “gain an unfair advantage”&lt;/li&gt;
      &lt;li&gt;Curiosity: “no one tells you about”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;How to escape the life you have (the simplest habit that changed everything)
    &lt;ul&gt;
      &lt;li&gt;Promise: “the simplest habit”&lt;/li&gt;
      &lt;li&gt;Outcome: “escape the life you hate”&lt;/li&gt;
      &lt;li&gt;Curiosity: “changed everything”&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get the point.&lt;/p&gt;

&lt;p&gt;It’s not a surprise why &lt;a href=&quot;/2025/04/26/MyMostReadHeadlines/&quot;&gt;some of my best headlines&lt;/a&gt; worked. They promised some value with a bit of curiosity.&lt;/p&gt;

&lt;p&gt;You only have a few seconds to hook readers. Start with a clear promise and outcome in your headlines. Don’t scare readers away with &lt;a href=&quot;/2025/06/04/OpeningLines/&quot;&gt;boring opening lines&lt;/a&gt;. Make your headlines impossible to ignore.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Remove This First Line If You Want Your Readers to Stay Past Your Opening Sentence</title>
   <link href="https://canro91.github.io/2025/06/04/OpeningLines/"/>
   <updated>2025-06-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/04/OpeningLines</id>
   <content type="html">&lt;p&gt;17ms. That’s how fast we decide whether to keep reading or move on.&lt;/p&gt;

&lt;p&gt;Credits to “Smart Brevity.”&lt;/p&gt;

&lt;h2 id=&quot;no-headline--no-opening-line--no-readers&quot;&gt;No headline + no opening line = no readers&lt;/h2&gt;

&lt;p&gt;If we don’t hook readers with a headline and an opening line, they will move on to the next search result or the next post in the feed.&lt;/p&gt;

&lt;p&gt;Yesterday, this opening line made me click away and open the next post in my RSS reader:&lt;/p&gt;

&lt;p&gt;“In today’s fast-moving constantly-changing economy…”&lt;/p&gt;

&lt;p&gt;And that’s not the first time I have found opening lines like that this week. “In our fast-paced world…” Nothing screams AI more than lines like those. Boooooring!&lt;/p&gt;

&lt;p&gt;When was the last time the economy didn’t change? And the last time our world was slow?&lt;/p&gt;

&lt;p&gt;Since we started doing accounting on pieces of clay in ancient Mesopotamia, the economy has been moving fast.&lt;/p&gt;

&lt;p&gt;And since God said “let there be light” (or the Big Bang started. Choose your own adventure), our world has been moving fast.&lt;/p&gt;

&lt;p&gt;There’s nothing surprising about “In today’s fast-paced blah blah blah.”&lt;/p&gt;

&lt;h2 id=&quot;start-with-blood-or-drama-start-with-the-unusual&quot;&gt;Start with blood or drama. Start with the unusual.&lt;/h2&gt;

&lt;p&gt;Remove the “in today’s blah, blah” part and keep the rest. You will have &lt;a href=&quot;/2025/05/09/BleedInTheFirstLine/&quot;&gt;a decent opening line&lt;/a&gt;. And you won’t give away that you are using AI.&lt;/p&gt;

&lt;p&gt;You only have a few seconds to hook readers before memes and cat pictures steal their attention. Make them count.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Strategies to Stay Sane in the Never-Ending Tech Hype Cycle</title>
   <link href="https://canro91.github.io/2025/06/03/Hype/"/>
   <updated>2025-06-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/03/Hype</id>
   <content type="html">&lt;p&gt;As developers, we can’t agree on much: tabs vs spaces, what “unit” really means in unit testing. But one thing we all agree on? There’s too much hype. And it’s exhausting.&lt;/p&gt;

&lt;p&gt;A new faster better AI tool promising to kill jobs, &lt;a href=&quot;/2024/12/08/CEOVsJanitor/&quot;&gt;“X% of code is generated by AI”&lt;/a&gt; at every FAANG, and the stock market reacting to those announcements.&lt;/p&gt;

&lt;p&gt;Olga asked on &lt;a href=&quot;https://dev.to/olgabraginskaya/how-do-you-stay-sane-with-the-non-stop-tech-hype-llms-anyone-5c86&quot;&gt;dev.to&lt;/a&gt; how we stay sane in all these hype.&lt;/p&gt;

&lt;p&gt;Here’s what I do to stay sane:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Pick the right battles.&lt;/strong&gt; After some years, we reach a point where we should find peace in what we choose to learn and what to ignore. I consider myself a backend developer and I’m fine missing out on every new JavaScript framework or “something.js” library out there. That was a battle I decided not to fight anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Start an information diet.&lt;/strong&gt; Find one single source of “official” information. Maybe company blogs or newsletters or trusted YouTube channels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Prefer long-form over short-form content.&lt;/strong&gt; Books go through a solid vetting process before they reach us. But social media content goes viral without any vetting process and builds up the hype.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Invest in what has passed the test of time.&lt;/strong&gt; What has already survived the test of time will outlive recent trends. We’re still using C/C++, SQL, and jQuery, and reading about stoicism. Chances are those same subjects will survive another decade or two. I wouldn’t bet all my money on it, though.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Wait for the dust to settle down.&lt;/strong&gt; Again, just see what stands after months or years. Xamarin, Silverlight, Flash… All of them are gone. And just last year, we got Devin, the first AI software engineer. &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;That was the first threat to our jobs&lt;/a&gt;. It was all over the headlines. These days? I haven’t heard about it since then.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Embrace just-in-time learning.&lt;/strong&gt; Instead of learning about every new shiny object, learn how to learn. And learn about new frameworks and tools in the context of real-world projects. &lt;a href=&quot;/2025/03/19/ChangedMyMind/&quot;&gt;I changed my mind&lt;/a&gt; about trying to learn about everything at once. Recently, I finished &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;Skip the Line&lt;/a&gt; and learned fast learning strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Understand there will always be a new hype.&lt;/strong&gt; I’ve seen the cloud, mobile apps, and now AI. And guess what? There were dozens before and there will be dozens more in the future. Let’s see what stands after the hype fades.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Biggest Obstacle Stopping People From Writing Online (And What to Do About It)</title>
   <link href="https://canro91.github.io/2025/06/02/WritingObstacle/"/>
   <updated>2025-06-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/02/WritingObstacle</id>
   <content type="html">&lt;p&gt;“I don’t want to expose myself,” an ex-coworker texted me.&lt;/p&gt;

&lt;p&gt;He had noticed &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;my LinkedIn posts&lt;/a&gt; and reached out to ask me how to start. He knew that writing opens doors but feared exposure. That’s one of &lt;a href=&quot;/2025/04/30/WritingMyths/&quot;&gt;the most common writing myths&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have the same concern:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. You don’t have to share selfies or post foot photos.&lt;/strong&gt; Selfies work best on Facebook or Instagram… And there are places for foot photos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. It takes time and consistency to make people care.&lt;/strong&gt; Your first post won’t make you an Internet celebrity. Magazines won’t write gossip articles about you. &lt;a href=&quot;/2025/01/02/SturgeonLaw/&quot;&gt;Your first post will be crap&lt;/a&gt;. (Mine were.) And nobody will care, not even your boss.&lt;/p&gt;

&lt;p&gt;And that’s fine. That’s not to discourage you, but to remove all the pressure of writing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Don’t share anything you wouldn’t mention in a work meeting.&lt;/strong&gt; You can keep your online presence as professional and personal as you want. If you’re a coder, &lt;a href=&quot;/2025/04/10/TilPosts/&quot;&gt;start sharing TIL posts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Forget about going viral. Forget about the influencer vibe. Write for one person. Write for your past self. Share what you’re learning. &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show your work&lt;/a&gt;. That’s the best way to start.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Simple Yet Effective Content Strategy and 5 Key Lessons from a Millionaire Creator</title>
   <link href="https://canro91.github.io/2025/06/01/DanKoe/"/>
   <updated>2025-06-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/06/01/DanKoe</id>
   <content type="html">&lt;p&gt;In 2024, I went all in with my writing. One of the biggest inspirations behind that leap was Dan Koe.&lt;/p&gt;

&lt;p&gt;He has more than 500K followers on X/Twitter, over 200 videos on his YouTube channel, and has sold millions online. He’s one of those internet names that made me believe making money online is possible.&lt;/p&gt;

&lt;p&gt;This time, I watched this interview between Dan Koe and Callum Johnson.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/g1GYGoMisE0?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Here are some of my takeaways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. A brand isn’t about colors, headshots, or taglines.&lt;/strong&gt; A brand is an environment you invite people into. It’s your goals, interests, and perspective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. If you don’t know where to start your journey, start with an anti-vision&lt;/strong&gt;: things you don’t want in your life. For most of us, that’s going back to a full-time job to work on something we don’t care about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Start by helping others solve a problem you already have solved.&lt;/strong&gt; And your first product could be an improved version of a product that transformed your life. Create it and promote it to your past self.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. A content strategy doesn’t have to be complicated.&lt;/strong&gt; One long-form piece repurposed into social media posts every week. And every social media post invites people to your newsletter, where you promote your products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Nothing is original.&lt;/strong&gt; Creating is about remixing ideas. And if you share it in your own words it’s not copying or stealing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. You don’t need to be discouraged if a “big name” or influencer offers something similar to what you offer.&lt;/strong&gt; That’s a faulty belief that assumes everyone knows and follows every influencer.&lt;/p&gt;

&lt;p&gt;I always remember one of Dan Koe’s quotes: &lt;em&gt;“The goal is being paid to be you.”&lt;/em&gt; That’s &lt;a href=&quot;/2025/01/06/BestQuote2024/&quot;&gt;one of my favorite quotes&lt;/a&gt;. A niche isn’t just about talking only about one topic forever. Your goals, mission, and transformation make you the niche. Don’t chase trends, create and sell to you. You are the niche.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>12 Nightmares Every Coder Faces (Junior or Senior, It Doesn&apos;t Matter)</title>
   <link href="https://canro91.github.io/2025/05/31/Nightmares/"/>
   <updated>2025-05-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/31/Nightmares</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;#1. A micromanaging boss.&lt;/strong&gt; Why trust your team members when you can ask them every 5 minutes how they’re doing?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. A spaghetti-style crappy codebase.&lt;/strong&gt; Why use good names and focused methods when you can copy-paste code and leave outdated comments all over the code? It’s faster that way, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Working on something nobody will use.&lt;/strong&gt; Why bother having your team create something valuable for users? They’re getting paid, anyway. &lt;a href=&quot;/2025/04/04/DemotivateYourTeam/&quot;&gt;They don’t need motivation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. QA only caring about colors, alignment, and fonts.&lt;/strong&gt; Who cares if the app works? QA should just test how it looks, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Too many meetings.&lt;/strong&gt; Daily stand-ups, sprint planning, poker estimation sessions, retrospectives, another meeting to present next sprint tickets, and another one just to answer questions from the previous meeting… They’re Software Meeting Attendees, not Software Developers, after all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Clueless project managers.&lt;/strong&gt; OK, you already explained to your PM how serious the issue you had to solve was and that’s why the sprint is behind schedule by one or two days. But they don’t seem to get it. It’s time to fire up ChatGPT and prompt it to explain like your PM is 5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Constantly being paged.&lt;/strong&gt; Why even write clean, organized code when you can fix everything while on-call rotation, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Not having time to refactor.&lt;/strong&gt; Again, why bother if we can fix all those issues while on-call rotation?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Code reviews taking too long.&lt;/strong&gt; A good title and description, and a short PR to make sure it’s easier to review it… But still more than 48 hours to get it approved and merged. Arrggg!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Constantly changing requirements.&lt;/strong&gt; &lt;em&gt;“What are you working on right now? Oh, there’s a new priority. Sales just promised a client a feature that wasn’t even in our roadmap…,”&lt;/em&gt; a random PM told a developer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#11. Repetitive tasks and grunt work.&lt;/strong&gt; Why bother using a computer to &lt;a href=&quot;/2025/03/10/AutomateCodeStyle/&quot;&gt;automate repetitive tasks and best practices&lt;/a&gt; if developers are cheap and fast code monkeys? A full regression testing cycle? Let’s make developers click on every button of the app. And let’s create a test case for every single text box of every single page. Manual labor is cheaper than automating it, right? They’re contractors paid by the hour, anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#12. Unrealistic deadlines.&lt;/strong&gt; &lt;em&gt;“Can we add a Facebook-style feed, I mean, a full Facebook-style feature, in the three days left in this sprint? It’s easy, right? Facebook already did it,”&lt;/em&gt; the same PM who asked for another priority just yesterday.&lt;/p&gt;

&lt;p&gt;If this feels familiar, I swear it’s just a wild coincidence.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Being replaced, job titles, and Haskell</title>
   <link href="https://canro91.github.io/2025/05/30/FridayLinks/"/>
   <updated>2025-05-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/30/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. This is not the first time we developers are being “replaced.” &lt;a href=&quot;https://alonso.network/the-recurring-cycle-of-developer-replacement-hype/&quot;&gt;There’s a recurring cycle of hype&lt;/a&gt; (6min). No code, the cloud, outsourcing, and these days AI. You name it!&lt;/p&gt;

&lt;p&gt;#2. Here’s a breakdown of &lt;a href=&quot;https://brainbaking.com/post/2025/05/software-engineering-job-titles/&quot;&gt;what every job title truly means&lt;/a&gt; (6min). I plead guilty of using &lt;em&gt;Senior [Insert Technology here] Developer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;#3. Don’t be discouraged about “Haskell” in the title. Here are some &lt;a href=&quot;https://chrispenner.ca/posts/interview&quot;&gt;silly job interview questions&lt;/a&gt; (15min). It’s a good exercise trying to reimplement them in your favorite language, using a more “functional” approach.&lt;/p&gt;

&lt;p&gt;#4. Starting from .NET10, we don’t need a solution or project file to run C# programs. We can simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet run app.cs&lt;/code&gt; Here’s &lt;a href=&quot;https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/&quot;&gt;the official announcement&lt;/a&gt; (5min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/05/27/SkipTheLine/&quot;&gt;six techniques to learn any skill faster&lt;/a&gt; (5min) and &lt;a href=&quot;/2025/05/29/Pizzas/&quot;&gt;why free pizzas aren’t a good idea to motivate a team&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Want to make your SQL Server go faster? Brent Ozar’s special anniversary sale is almost over. Only two days left.&lt;/p&gt;

&lt;p&gt;Until May 31st, get the &lt;a href=&quot;https://training.brentozar.com/p/recorded-class-season-pass-masters-classes?affcode=920087_fhe3khrq&quot;&gt;prerecorded Mastering bundle&lt;/a&gt; and save $400. This bundle includes four advanced courses covering indexes, queries, parameter sniffing, and full database optimization. SQL Server was my kryptonite until I learned databases from Brent’s courses, and I highly recommend them. Grab the bundle before the sale ends.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Your Dev Team Doesn&apos;t Want Pizzas on Fridays for Motivation</title>
   <link href="https://canro91.github.io/2025/05/29/Pizzas/"/>
   <updated>2025-05-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/29/Pizzas</id>
   <content type="html">&lt;p&gt;“The one who finishes all their tasks early can go home,” they told us.&lt;/p&gt;

&lt;p&gt;It was at a past job, some time ago, in a galaxy far far away. That was their incentive to make us work harder. We were behind our deadlines and the local government’s. We were stuck in a rewrite within a rewrite.&lt;/p&gt;

&lt;h2 id=&quot;but-surprise-surprise&quot;&gt;But, surprise, surprise…&lt;/h2&gt;

&lt;p&gt;One guy finished all his tasks before the end of our 2-week sprint. I don’t know if he worked from home or padded all his estimations.&lt;/p&gt;

&lt;p&gt;But they hesitated to give him the time off. We could tell by looking at our leader’s face when that guy handed in the time-off request form.&lt;/p&gt;

&lt;p&gt;The next time the same guy finished earlier, they pulled more tasks from the backlog or ask him to join the on-call rotation. It was a carrot in front of the donkey.&lt;/p&gt;

&lt;p&gt;Instead of building morale, they destroyed it and made all the trust evaporate.&lt;/p&gt;

&lt;h2 id=&quot;your-development-team-doesnt-want-free-pizzas&quot;&gt;Your development team doesn’t want free pizzas.&lt;/h2&gt;

&lt;p&gt;Oh, by the way, pizza was the next strategy after the “time off if you finish your task” strategy. Close to 100% sprint completion = free pizzas.&lt;/p&gt;

&lt;p&gt;Instead of pizzas, give your team interesting work. Give them a challenge and share how that will impact the company. Give them room to figure out things on their own. And if they screw things up, don’t point fingers, let them fix things. Trust them. They’re human beings, not disposable resources. Motivation isn’t about perks. It’s about trust. Build that first.&lt;/p&gt;

&lt;p&gt;And if nothing of that works, here’s &lt;a href=&quot;/2025/04/04/DemotivateYourTeam/&quot;&gt;how to demotivate them&lt;/a&gt;. Do the opposite.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Spanish Slang for Money—And What It Can Tell Us About Our Future</title>
   <link href="https://canro91.github.io/2025/05/28/Money/"/>
   <updated>2025-05-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/28/Money</id>
   <content type="html">&lt;p&gt;The other day, I jumped into a discussion on LinkedIn about slang for money.&lt;/p&gt;

&lt;p&gt;I shared the Spanish slang for money:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Plata. Same word for silver and money.&lt;/li&gt;
  &lt;li&gt;Pavos, turkeys.&lt;/li&gt;
  &lt;li&gt;Lana, wool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And in some regions of my country, to designate millions:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Melones, melons. Yes, just like the fruit.&lt;/li&gt;
  &lt;li&gt;Palos, sticks. Yes, like pieces of wood.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While writing my comment on LinkedIn, I realized those words represent goods we used for value exchange centuries ago when we didn’t have coins or pieces of paper. I don’t know the exact origin of those words. But it makes sense, right?&lt;/p&gt;

&lt;p&gt;These days, &lt;a href=&quot;/2025/02/01/NewDefinitionOfMoney/&quot;&gt;our definition of money has changed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Time and attention are our most valuable assets now. The book “Your Money or Your Life” defines money as what we trade in exchange for our time. Maybe it isn’t crazy to imagine a future where “seconds of watching ads” or “minutes of doomscrolling” become our new currency. Not dollars or euros or pesos.&lt;/p&gt;

&lt;p&gt;Maybe one day paying for dinner will cost an hour of attention or two. Scrolling, clicking, and absorbing ads designed to keep you hooked.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Six Proven Principles to Learn Any Skill Faster (Without Spending 10,000 Hours)</title>
   <link href="https://canro91.github.io/2025/05/27/SkipTheLine/"/>
   <updated>2025-05-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/27/SkipTheLine</id>
   <content type="html">&lt;p&gt;Since 2020, we’re living in a new normal.&lt;/p&gt;

&lt;p&gt;Constant layoffs, market volatility, AI stealing our jobs… The best thing to do is to learn new skills and build something we can’t be fired from.&lt;/p&gt;

&lt;p&gt;But who has time to spend 10,000 hours becoming an expert to monetize a new skill?&lt;/p&gt;

&lt;p&gt;Here’s where “Skip the Line” by &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt; comes in. A book with strategies to ditch those 10,000 hours and learn a new skill faster, without going back for 4 or 5 years of college or begging to get into the small circle of “experts.”&lt;/p&gt;

&lt;p&gt;Here are 6 principles I learned from “Skip The Line” to master any skill faster:&lt;/p&gt;

&lt;h2 id=&quot;1-do-10000-experiments&quot;&gt;1. Do 10,000 experiments&lt;/h2&gt;

&lt;p&gt;It takes 10,000 hours to be an “expert.”&lt;/p&gt;

&lt;p&gt;That’s about 10 years of part-time practice. But the world is changing so fast. By the time you’ve put in those 10,000 hours, everything will be different.&lt;/p&gt;

&lt;p&gt;Instead of tracking hours, track experiments. Small and consistent actions that teach you a skill faster.&lt;/p&gt;

&lt;p&gt;A good experiment:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;is easy to set up and do&lt;/li&gt;
  &lt;li&gt;has little downside&lt;/li&gt;
  &lt;li&gt;has huge potential upside&lt;/li&gt;
  &lt;li&gt;has never been done before&lt;/li&gt;
  &lt;li&gt;teaches you something&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“You know something is a valid experiment when you take out what you normally do, get curious about an idea, as in “What if I try…,” and then, you suddenly feel fear.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With experiments and real projects, we learn way more and faster than in any classroom. Remember &lt;a href=&quot;/2024/11/02/PassiveLearning/&quot;&gt;passive learning is just entertainment&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-master-your-microskills&quot;&gt;2. Master your microskills&lt;/h2&gt;

&lt;p&gt;Whatever you’d like to learn, chances are it’s not a single skill, but a range of microskills.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Break apart a skill into 20 microskills. Figure out how, each day, you can get better at each microskill”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writing isn’t just one skill. It’s headlines, openings, storytelling, calls to action, editing, formatting… And that’s just non-fiction.&lt;/p&gt;

&lt;h2 id=&quot;3-master-idea-calculus&quot;&gt;3. Master idea calculus&lt;/h2&gt;

&lt;p&gt;You can cheat your way through those 10,000 hours by bringing your expertise from other areas.&lt;/p&gt;

&lt;p&gt;Add a new idea or concept to an existing idea in the field you’re learning. Or subtract an existing idea from it. Or mix two disparate ideas from another field to create a new one.&lt;/p&gt;

&lt;h2 id=&quot;4-find-plus-equal-and-minus&quot;&gt;4. Find PLUS, EQUAL, and MINUS&lt;/h2&gt;

&lt;p&gt;To learn anything faster, you need to find your PLUS, EQUAL, and MINUS:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;PLUS are your mentors. People online or offline who you can learn from. If &lt;a href=&quot;/2024/11/26/FindingMentors/&quot;&gt;you can’t find mentors&lt;/a&gt;, books are always good mentors.&lt;/li&gt;
  &lt;li&gt;EQUAL are others learning the same skill. These are the people who challenge you and encourage you to keep learning.&lt;/li&gt;
  &lt;li&gt;MINUS are people you can teach. Teaching is the best (and fastest) way to learn anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-follow-the-501-rule&quot;&gt;5. Follow the 50/1 rule&lt;/h2&gt;

&lt;p&gt;The 80/20 rule has a close cousin.&lt;/p&gt;

&lt;p&gt;You know the 80/20 rule, right? 80% of wealth is accumulated by 20% of people. 80% of outcomes come from 20% of effort. 80% of views and reads come from 20% of posts.&lt;/p&gt;

&lt;p&gt;But what if we apply the 80/20 rule to itself? 80% of 80 is 64, and 20% of 20 is 4.&lt;/p&gt;

&lt;p&gt;And if we apply it again? 80% of 64 is 51.2 and 20% of 4 is 0.8. OK, let’s round that up to 50 and 1.&lt;/p&gt;

&lt;p&gt;It means that 1% of effort brings 50% of outcomes. That’s a cheat code to get results faster.&lt;/p&gt;

&lt;p&gt;If you’re writing, that’s your headline, opening lines, and first paragraphs. Or your titles and thumbnails if you’re doing YouTube.&lt;/p&gt;

&lt;p&gt;Whatever you’re doing, find your 1%.&lt;/p&gt;

&lt;h2 id=&quot;6-find-your-wheel-and-its-spokes&quot;&gt;6. Find your wheel and its spokes&lt;/h2&gt;

&lt;p&gt;One thing is mastering a skill. Another is making money off of it.&lt;/p&gt;

&lt;p&gt;Find your wheel, your main subject or skill. Once you’ve found your wheel, look for spokes. Ways to monetize and expand your wheel.&lt;/p&gt;

&lt;p&gt;If your wheel is photography, your spokes could be:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sell stock photos,&lt;/li&gt;
  &lt;li&gt;Offer wedding photography services,&lt;/li&gt;
  &lt;li&gt;Shoot product images for small businesses,&lt;/li&gt;
  &lt;li&gt;Shoot LinkedIn-optimized portraits for executives,&lt;/li&gt;
  &lt;li&gt;Offer walking tours through the most scenic spots in your city,&lt;/li&gt;
  &lt;li&gt;Teach a budget-friendly photography course for beauty creators,&lt;/li&gt;
  &lt;li&gt;Offer “I fix and enhance old photos” services,&lt;/li&gt;
  &lt;li&gt;Create a course for better pet photography,&lt;/li&gt;
  &lt;li&gt;Start a newborn photography business,&lt;/li&gt;
  &lt;li&gt;Run workshops to teach others,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, I already wrote &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;my 10 bad ideas&lt;/a&gt;  for today… And you got the point. There’s more than one way to monetize a skill. Don’t marry a single one. I’m talking about ideas here.&lt;/p&gt;

&lt;p&gt;The world still needs experts. No doubt. I wouldn’t want guesswork in an operating room. But you don’t need to be an expert to make a living. You don’t need 10,000 hours. You need more experiments, faster feedback loops. This new world is for fast learners, for those who know how to skip the lines.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The One Writing Trick to Make Your Content Clickbait-Free</title>
   <link href="https://canro91.github.io/2025/05/26/TitleDrivenCreation/"/>
   <updated>2025-05-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/26/TitleDrivenCreation</id>
   <content type="html">&lt;p&gt;No matter how thoughtful your content is, without a strong headline, people won’t read it.&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;https://dynomight.net/titles/&quot;&gt;How to title your blog post or whatever&lt;/a&gt;, there’s this tip for headlines or titles:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Consider title-driven thing creation. That is, consider first choosing a title and then creating a thing that delivers on the title. It’s sad to admit, but I think there are many good things that simply don’t have good titles. Consider not making those things… In practice, it’s often something in the middle: You start to create your thing, then you choose a title, then you structure your thing to deliver on the title.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Title-driven creation not only applies to blog posts, but also to other forms of creative output:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Before making a course or paid product, write its landing page.&lt;/li&gt;
  &lt;li&gt;Before &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;writing a book&lt;/a&gt;, choose a title, write a summary, and design its cover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before creating, write the marketing materials first. For blog posts and social media content, that’s the headline.&lt;/p&gt;

&lt;p&gt;Every headline is a promise. Keep your promises, and your readers will trust you.&lt;/p&gt;

&lt;p&gt;Here are some of &lt;a href=&quot;/2025/04/26/MyMostReadHeadlines/&quot;&gt;my best healdines&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Need to Speak in Public? Write a Good Post First</title>
   <link href="https://canro91.github.io/2025/05/25/PublicSpeak/"/>
   <updated>2025-05-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/25/PublicSpeak</id>
   <content type="html">&lt;p&gt;Public speaking starts with a clear message.&lt;/p&gt;

&lt;p&gt;If you need to speak publicly:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Start with a catchy story&lt;/li&gt;
  &lt;li&gt;Share one main idea&lt;/li&gt;
  &lt;li&gt;Use three points to expand your main idea. Yes, three points. Our short-term memory holds around 5 or 7 items, and three is the sweet spot.&lt;/li&gt;
  &lt;li&gt;End by inviting listeners to do something about it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that already sounds like a good post. A good post should have a clear message too.&lt;/p&gt;

&lt;p&gt;Even better, if you are already writing using your own voice, print your post and read it in front of your audience, making the right pauses and changes in intonation, of course.&lt;/p&gt;

&lt;p&gt;And, don’t use a PowerPoint presentation. I learned that from &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, from one of his interviews, I don’t remember which one. A PowerPoint presentation will distract the audience from the message: you and your speech.&lt;/p&gt;

&lt;p&gt;That works for a TED talk, a keynote speech, or a sermon.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Ways Copilot Helped Me Code Smarter This Week</title>
   <link href="https://canro91.github.io/2025/05/24/CodingWithAI/"/>
   <updated>2025-05-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/24/CodingWithAI</id>
   <content type="html">&lt;p&gt;Rejecting AI is like rejecting calculators or computers.&lt;/p&gt;

&lt;p&gt;It’s here to stay and &lt;a href=&quot;/2025/01/21/AIAdaptation/&quot;&gt;we have to adapt&lt;/a&gt;. That’s why I always keeping an AI chat open, looking for ways to automate my work. Or maybe I’m just getting lazier as I get older.&lt;/p&gt;

&lt;p&gt;This week, I tried Copilot (on the browser) to help me with some coding tasks. Here they are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Translate a Visual Basic code block into C#.&lt;/strong&gt; I’m working with an old legacy WebForms app in Visual Basic these days. And being honest, I’m not in the mood for learning Visual Basic, so why not just translate it using AI? And as a form of exploratory refactoring, I gave it a large convoluted block of code and asked it to explain what it does and refactor it into smaller functions. And once I understood what it did, I threw away the refactored version. Sorry, Copilot!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Create a C# extension method from a code block.&lt;/strong&gt; Sometimes I’m just lazy as an excuse to test Copilot’s capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3.&lt;/strong&gt; Naming is one of the most difficult tasks. &lt;strong&gt;So why not ask Copilot for help to come up with descriptive names?&lt;/strong&gt; I gave Copilot a class definition, explained its purpose, and asked for a list of better names. I ended up choosing one of its options and got my PR approved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Generate Builders for test data.&lt;/strong&gt; I gave it a sample &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;Builder class&lt;/a&gt; and asked it to follow the same pattern but for some methods of a different class. Copilot nailed it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Replicate tests from a sample test class.&lt;/strong&gt; While refactoring away from logic-heavy controllers to handlers, I gave it an existing handler and its tests. Then, after giving it the signature of my new handler, I asked Copilot to rewrite my controller tests as handler tests, following the pattern from the other handler tests and keeping &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;my original assertions&lt;/a&gt;. Again, Copilot nailed this one.&lt;/p&gt;

&lt;p&gt;And just for the record, I’m not using any fancy prompt, but this one,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Act as a senior software engineer, with expert knowledge of the .NET stack, its libraries and ecosystem, and clean code. &amp;lt;Explain task here&amp;gt;”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve found better results when I gave Copilot a sample class or code block to replicate. Think of AI as a fast junior engineer that needs clear and precise instructions. Otherwise, it takes a wild guess instead of asking for clarification.&lt;/p&gt;

&lt;p&gt;Also as an experiment, I used &lt;a href=&quot;/2024/03/18/AIToLaunchMyCourses/&quot;&gt;Copilot to launch a coding course&lt;/a&gt;. And once &lt;a href=&quot;/2024/12/20/CopywritingStudyPlan/&quot;&gt;I learned enough copywriting&lt;/a&gt;, I revisited some of the marketing materials I generated with Copilot. And recently, I’ve been &lt;a href=&quot;/2025/03/01/ReplacingGrammarly/&quot;&gt;using a prompt to replace Grammarly&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Microsoft, AI, and localization</title>
   <link href="https://canro91.github.io/2025/05/23/FridayLinks/"/>
   <updated>2025-05-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/23/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. Microsoft has made a lot of noise this week. It laid off around ~6K people. And it made &lt;a href=&quot;https://blogs.windows.com/windowsdeveloper/2025/05/19/the-windows-subsystem-for-linux-is-now-open-source/&quot;&gt;the Windows Subsystem for Linux&lt;/a&gt; (5min) and &lt;a href=&quot;https://code.visualstudio.com/blogs/2025/05/19/openSourceAIEditor&quot;&gt;the Copilot Chat extension&lt;/a&gt; (8min) open source. It seems the layoffs and the release of open source aren’t related. At least, that’s the official version.&lt;/p&gt;

&lt;p&gt;#2. Speaking of Microsoft, they enabled Copilot on the Runtime repository. And it seems &lt;a href=&quot;https://nmn.gl/blog/ai-scam&quot;&gt;AI can’t fix simple bugs&lt;/a&gt; (4min).&lt;/p&gt;

&lt;p&gt;#3. It seems I’m not the only one who doesn’t like it when web apps use &lt;a href=&quot;https://vitonsky.net/blog/2025/05/17/language-detection/&quot;&gt;my location to decide what language to use&lt;/a&gt;. There’s already a better alternative to using IP addresses.&lt;/p&gt;

&lt;p&gt;#4. One of the most common mistakes when writing tests is duplicating the logic under test inside assertions. That’s what’s called &lt;a href=&quot;https://laser-coder.net/articles/circular-reasoning/index.html&quot;&gt;circular reasoning&lt;/a&gt; (7min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/05/17/NeedForScale/&quot;&gt;stop optimizing for scale when we don’t need it&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/05/19/StressfulJob/&quot;&gt;the key to survive a stressful job&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Want to make your SQL Server go faster? Brent Ozar, the leading SQL Server expert, is celebrating with a special anniversary sale.&lt;/p&gt;

&lt;p&gt;Until May 31st, get the &lt;a href=&quot;https://training.brentozar.com/p/recorded-class-season-pass-masters-classes?affcode=920087_fhe3khrq&quot;&gt;prerecorded Mastering bundle&lt;/a&gt; and save $400. This bundle includes four advanced courses covering indexes, queries, parameter sniffing, and full database optimization. Everything you need to make your SQL Server fly.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>If You Had 12 Months to Live, What Would You Do? Here&apos;s What I&apos;d Do</title>
   <link href="https://canro91.github.io/2025/05/22/12Months/"/>
   <updated>2025-05-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/22/12Months</id>
   <content type="html">&lt;p&gt;I’ve heard life only guarantees two things: death and taxes.&lt;/p&gt;

&lt;p&gt;One happens every year. The other? Just once.&lt;/p&gt;

&lt;p&gt;As a thought experiment and writing exercise, if I only had 12 months to live, assuming I’m in good shape, mentally and physically, these are 7 actions I’d do:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Transfer 50% of my retirement savings and net worth to my closest relatives.&lt;/strong&gt; I want to make sure they don’t have to worry about covering their essential expenses for a while.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Spend the other 50% of my net worth without any guilt.&lt;/strong&gt; I’d backpack around Europe and South America. I definitely would walk El Camino de Santiago, the pilgrimage route along Spain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Share my blog and social media credentials&lt;/strong&gt; with a loved one so my online presence outlives me and keeps generating income for my relatives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Write a book.&lt;/strong&gt; The other day, while &lt;a href=&quot;https://www.youtube.com/watch?v=sHfrvpgDsaw&quot;&gt;watching Chandler Bolt’s TED talk&lt;/a&gt;, I learned that &lt;em&gt;“the best way you can serve humanity is to write a book.”&lt;/em&gt; I don’t expect it to become a best-seller. I just want to make sure part of my story is written somewhere for my relatives to read. &lt;a href=&quot;/2025/04/22/BookForGrandkids/&quot;&gt;That’s a good motivation to write a book&lt;/a&gt;. Hey if we’re still reading diaries from ancient Greece, maybe someone will read my blog or book centuries in the future. Who knows?!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5.&lt;/strong&gt; And, in the same spirit of sharing my story, &lt;strong&gt;take a picture every day for the remaining 12 months.&lt;/strong&gt; Like a photo blog or something. Especially while walking El Camino de Santiago. That’s a perfect idea for a visual memoir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Write a letter to every family member&lt;/strong&gt;, scheduled to be delivered after those 12 months. Okay, I know that sounds like the plot of a romantic movie.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Get an organ donor card.&lt;/strong&gt; My organs will help somebody else more than me by the end of those 12 months.&lt;/p&gt;

&lt;p&gt;I just realized I don’t need to wait to do most of these things.&lt;/p&gt;

&lt;p&gt;Oh, this list reminded me of &lt;a href=&quot;/2025/05/18/HouseMD/&quot;&gt;one House M.D. episode&lt;/a&gt; where Dr. Wilson, an oncologist and Dr. House’s best friend, made a mistake and gave the wrong prescription to a patient. He told the patient he only had a few months to live. And when the patient was told about the mistake, he got angry because his family had already scheduled trips and celebrations. His life had already changed because of the prescription.&lt;/p&gt;

&lt;p&gt;Why does life have to change when the clock is close to running out of batteries? Why wait?! Ok, maybe I got too philosophical. And I already got more than my 200 words for the day.&lt;/p&gt;

&lt;p&gt;PS: This was &lt;a href=&quot;https://ronwayjourney.medium.com/what-if-you-had-12-months-to-live-what-would-you-do-661f6f8db873&quot;&gt;a writing prompt from Ron Markley&lt;/a&gt; on Medium.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This Robert Greene Quote Made Me Reflect on My Sources of Inspiration</title>
   <link href="https://canro91.github.io/2025/05/21/Material/"/>
   <updated>2025-05-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/21/Material</id>
   <content type="html">&lt;p&gt;I know we’re not supposed to multitask.&lt;/p&gt;

&lt;p&gt;But recently, while doing the dishes, I listened to &lt;a href=&quot;https://www.youtube.com/watch?v=HSgLWVPmfqI&quot;&gt;this YouTube interview&lt;/a&gt; between Ryan Holiday and Robert Greene, two best-selling writers.&lt;/p&gt;

&lt;p&gt;Out of all the subjects they talked about, there’s one line that I still remember:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“It’s all material”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They both share how problematic people and unexpected situations end up being material for an email or a book.&lt;/p&gt;

&lt;h2 id=&quot;and-that-line-made-me-think-about-unexpected-situations-that-have-inspired-me-to-write&quot;&gt;And that line made me think about unexpected situations that have inspired me to write.&lt;/h2&gt;

&lt;p&gt;#1. The other day I took a loved one to a hospital. And I ended up writing about &lt;a href=&quot;/2025/04/19/CalmDown/&quot;&gt;calming my mind&lt;/a&gt;, &lt;a href=&quot;/2025/04/20/MedAI/&quot;&gt;the future of hospital visits with AI&lt;/a&gt;, and &lt;a href=&quot;/2025/05/08/RandomLessons/&quot;&gt;random facts about health&lt;/a&gt;, which I learned on that visit.&lt;/p&gt;

&lt;p&gt;#2. Another day, a cashier overheard a conversation I had with a friend, and her reaction inspired me to write about &lt;a href=&quot;/2025/04/08/SuccessMetrics/&quot;&gt;our definition of success&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#3. I used to feel guilty about watching movies and series. But when I started watching them through a creator’s lens, I realized how much they could teach about storytelling and writing. I’ve watched and written about &lt;a href=&quot;/2025/03/13/SixTripleEight/&quot;&gt;Six Triple Eight&lt;/a&gt;, &lt;a href=&quot;/2024/12/09/Scorpion/&quot;&gt;Scorpion&lt;/a&gt;, and &lt;a href=&quot;/2025/05/18/HouseMD/&quot;&gt;House M.D.&lt;/a&gt;. No more guilt. Just writing lessons. After all, It’s all material.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Thought I&apos;d Run Out of Ideas—but 200 Posts Proved Me Wrong</title>
   <link href="https://canro91.github.io/2025/05/20/200DailyPosts/"/>
   <updated>2025-05-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/20/200DailyPosts</id>
   <content type="html">&lt;p&gt;I decided to challenge myself: write every single day. No excuses.&lt;/p&gt;

&lt;p&gt;I started back in &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;November 1st, 2024&lt;/a&gt;. This is my daily post #200. I thought I didn’t have anything to share. If we think a post has to be 100% original, we seldom write. And the truth is there’s nothing 100% original.&lt;/p&gt;

&lt;p&gt;I also thought I’d run out of ideas. But &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;the first 100 daily posts&lt;/a&gt; proved me wrong and another 100 convinced me it’s possible.&lt;/p&gt;

&lt;h2 id=&quot;my-most-read-and-favorite-posts&quot;&gt;My most-read and favorite posts&lt;/h2&gt;

&lt;p&gt;Here are some of the most-read posts from the last 100 days:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/04/DemotivateYourTeam/&quot;&gt;How to Demotivate Your Development Team&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;10 Ways to Stand Out at Work—Other than Work Hard&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/03/19/ChangedMyMind/&quot;&gt;9 Subjects I’ve Changed My Mind About as a Software Engineer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/28/Frustrations/&quot;&gt;What Frustrates Me the Most as a C#/.NET Developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/02/19/YouAreNotAProgrammerUntil/&quot;&gt;You’re Not a Programmer Until…&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here are some of my favorites:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/20/MedAI/&quot;&gt;How MedAI Will Transform Hospitals and Patient Care by 2035&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/17/HarshTruths/&quot;&gt;12 Hard Truths About Coding I Learned the Hard Way After 10 Years&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/03/23/BecomingAnIdeaMachine/&quot;&gt;9 Lessons from Writing 10 (Bad) Ideas Daily to Become an Idea Machine&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/02/KidneysDoMatter/&quot;&gt;I Never Knew Kidneys Mattered—Until Dialysis Hit Close to Home&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/03/JesusEffect/&quot;&gt;Why You Should Change Jobs—According to Jesus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-posting-daily-has-done-for-me&quot;&gt;What posting daily has done for me&lt;/h2&gt;

&lt;p&gt;Posting daily has:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Made me conscious of the content I consume&lt;/li&gt;
  &lt;li&gt;Made me think of posts as public notes&lt;/li&gt;
  &lt;li&gt;Made me remember past stories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some days I almost broke my streak. Some night, near mid-night, I had to rush to finish my post for the next day.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;Writing 10 ideas a day&lt;/a&gt; to become an idea machine has helped me keep my writing goal. Some of my 10-idea lists have inspired or become posts.&lt;/p&gt;

&lt;p&gt;Writing daily has become part of my healthy routine. It has sharpened my writing skills and helped me document ideas I would have otherwise forgotten.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Key to Surviving a Stressful Job (While You Land a New One)</title>
   <link href="https://canro91.github.io/2025/05/19/StressfulJob/"/>
   <updated>2025-05-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/19/StressfulJob</id>
   <content type="html">&lt;p&gt;A stressful job can take a serious toll on your mental health.&lt;/p&gt;

&lt;p&gt;I know because I’ve had one. In that job, we were chasing our own deadlines, our clients’ expectations, and local government regulations. We were late all the time. Everyone blamed the Development team. Leaders constantly pressured us.&lt;/p&gt;

&lt;p&gt;Working after hours was normal. We called them “Miracle Nights.” In one night, we did way more than we did in a sprint. You can imagine the amount of technical debt we were leaving behind.&lt;/p&gt;

&lt;p&gt;While working on that job, I ate &lt;a href=&quot;/2024/12/11/SelfManagedTeam/&quot;&gt;the most expensive hamburgers I’ve ever had&lt;/a&gt;, by the way.&lt;/p&gt;

&lt;h2 id=&quot;but-there-was-something-that-made-everything-easier&quot;&gt;But there was something that made everything easier&lt;/h2&gt;

&lt;p&gt;Our relationships and friendships made all the difference.&lt;/p&gt;

&lt;p&gt;It was having someone who could listen, a group for Friday nights, and friends who could refer you to new jobs. That support changed everything.&lt;/p&gt;

&lt;p&gt;Even though it was one of my most stressful jobs, I did the most to take care of my mental health while working there. It was easier because I had a “support group,” coworkers that eventually turned into friends.&lt;/p&gt;

&lt;p&gt;If you’re working on a stressful job or not, make an extra effort to build those professional relationships. The world runs on connections. And in the end, we’re remembered not just for our work, but for how we treat those around us.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Storytelling Secrets from One of My Favorite House M.D. Episodes</title>
   <link href="https://canro91.github.io/2025/05/18/HouseMD/"/>
   <updated>2025-05-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/18/HouseMD</id>
   <content type="html">&lt;p&gt;I used to feel guilty about watching Netflix and TV shows.&lt;/p&gt;

&lt;p&gt;I felt like I was wasting time when I could be grinding. But when I started to seeing things through a writer’s lens, Netflix and TV shows became a source of storytelling lessons.&lt;/p&gt;

&lt;p&gt;I’ve been binge-watching House M.D. this year. I’m on season 7 (out of 8). No guilt.&lt;/p&gt;

&lt;p&gt;Recently, I watched episode 13 of season 7, “Two Stories.” It’s one of my favorite episodes.&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of the storytelling and plot from that episode—no spoilers:&lt;/p&gt;

&lt;h2 id=&quot;1&quot;&gt;#1.&lt;/h2&gt;

&lt;p&gt;The episode starts with a schoolboy and schoolgirl getting into trouble and being taken to the principal’s office.&lt;/p&gt;

&lt;p&gt;And guess who’s also there waiting for the principal? Yes, Dr. House. How the heck did he end up there? That’s the whole point of this episode.&lt;/p&gt;

&lt;p&gt;Dr. House is intrigued by why the two students are there.&lt;/p&gt;

&lt;p&gt;They made a deal: they’d explain why they’re there, only if he revealed why he was there too. Of course, he’s not a student.&lt;/p&gt;

&lt;h2 id=&quot;2&quot;&gt;#2.&lt;/h2&gt;

&lt;p&gt;Dr. House starts to tell them how he got to the principal’s office.&lt;/p&gt;

&lt;p&gt;Then, we’re taken on a flashback to a couple of hours before. He’s in front of a classroom telling the students about what he does and about his most recent case.&lt;/p&gt;

&lt;p&gt;This is the clever, ingenious, and my favorite part:&lt;/p&gt;

&lt;p&gt;Dr. House is telling us the episode’s backstory while he’s telling other stories: the conversation outside the principal’s office, the flashbacks, and his speech in front of the classroom.&lt;/p&gt;

&lt;h2 id=&quot;3&quot;&gt;#3.&lt;/h2&gt;

&lt;p&gt;While sharing his most recent case in front of the classroom, Dr. House starts to recreate scenes from famous movies.&lt;/p&gt;

&lt;p&gt;And, in the classroom, there’s a movie lover who recognizes every scene. That only makes the students more interested in what Dr. House has to say.&lt;/p&gt;

&lt;p&gt;Apart from Dr. House, there are three more guests. It seems they’re a police officer, a counselor, and a businessman. Who the heck are they? That’s something revealed at the end.&lt;/p&gt;

&lt;p&gt;Here’s a perfect example of Dr. House’s in front of the class:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/a1UZRzbiE8Y?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Dr. House’s storytelling is so captivating that the students keep asking questions, leaving no room for the other guests to speak.&lt;/p&gt;

&lt;h2 id=&quot;4&quot;&gt;#4.&lt;/h2&gt;

&lt;p&gt;In the meantime, we’re taken back to the principal’s office.&lt;/p&gt;

&lt;p&gt;The two students also begin sharing why they’re there. An innocent plot from a boy trying to get closer to his crush. I won’t spoil the details.&lt;/p&gt;

&lt;p&gt;The episode jumps between their story and Dr. House’s story. All told in flashbacks.&lt;/p&gt;

&lt;h2 id=&quot;5&quot;&gt;#5.&lt;/h2&gt;

&lt;p&gt;Finally, all the stories connect.&lt;/p&gt;

&lt;p&gt;We learn why Dr. House is there, who the businessman in the room is, and the clue that solves Dr. House’s case.&lt;/p&gt;

&lt;p&gt;Oh, I forgot to mention:&lt;/p&gt;

&lt;p&gt;All that time, Dr. House had a case. He was trying to solve it via phone calls, while his team was back at the hospital dealing with the patient’s complications.&lt;/p&gt;

&lt;h2 id=&quot;my-lessons&quot;&gt;My lessons?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Start by grabbing people’s attention with drama or mystery.&lt;/strong&gt; For example, why is Dr. House outside a school principal’s office?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Tell stories in such a way that people have to ask “What’s next?” or “Wait a second, what about that?”&lt;/strong&gt; Use suspense, cliffhangers, and unanswered questions. Always make them want more.&lt;/p&gt;

&lt;p&gt;In front of the class, Dr. House shares clues that are left unresolved. He was trying to hack into a laptop. The students kept asking why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A proven plot to follow:&lt;/strong&gt; Make your characters have a conversation. Use the conversation to introduce flashbacks and tell about one of the characters’ past. Then, make all the flashbacks concur in a resolving scene that puts everyone back into the initial conversation.&lt;/p&gt;

&lt;p&gt;Wait, it seems I just described Forrest Gump: a conversation on a bench, lots of flashbacks that explain the backstory, and a resolution that ties everything together. Turns out, great stories follow a blueprint.&lt;/p&gt;

&lt;p&gt;By the way, here’s the breakdown of &lt;a href=&quot;/2025/03/13/SixTripleEight/&quot;&gt;Six Triple Eight&lt;/a&gt;, a Netflix show I also watched with my writer’s lens on.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stop Optimizing for Scale—Most Apps Don&apos;t Need It</title>
   <link href="https://canro91.github.io/2025/05/17/NeedForScale/"/>
   <updated>2025-05-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/17/NeedForScale</id>
   <content type="html">&lt;p&gt;In &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;my Friday Links email&lt;/a&gt; yesterday, I shared one post called &lt;a href=&quot;https://amritpandey.io/programming-myths-we-desperately-need-to-retire/&quot;&gt;Programming Myths We Desperately Need to Retire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Myth #5, “Let’s optimize for scale,” resonated a lot with me:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Most products never reach the scale you’re “preparing” for. And even if they do, you’ll have time—and budget—to refactor later.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oh boy! Been there and done that.&lt;/p&gt;

&lt;p&gt;At a past job, while working on a hotel management solution, we had to connect the reservation module to a third-party restaurant solution. The goal was for guests to pay their restaurant bills along with their hotel bills.&lt;/p&gt;

&lt;p&gt;We were about six software engineers plus the QA team. We worked to make that solution scale to thousands of guests and restaurant orders. We built an eventually consistent solution with &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;background processors using Hangfire&lt;/a&gt;, &lt;a href=&quot;/2025/03/16/DDDIsNotAboutEntities/&quot;&gt;Domain-Driven Design&lt;/a&gt;, and all the best practices you can imagine.&lt;/p&gt;

&lt;p&gt;There’s nothing wrong with that. The problem? When we finished our module and handed it off to the Product people. Nobody bought it. Six months of work…wasted.&lt;/p&gt;

&lt;p&gt;The Product team only had a few leads when they pitched the idea to the Engineering team. But those leads weren’t going to wait six months for our integration. Or maybe the Product team wanted to list a new feature on the webpage so they could compare the product to the competitors. And after all that time optimizing for scale? Nobody bought it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Coding myths, a pwned dating app, and Visual Basic</title>
   <link href="https://canro91.github.io/2025/05/16/FridayLinks/"/>
   <updated>2025-05-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/16/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. We as coders take pride in our craft. We like to follow principles and best practices: KISS, SOLID, DRY. But there are &lt;a href=&quot;https://amritpandey.io/programming-myths-we-desperately-need-to-retire/&quot;&gt;some coding myths we need to retire&lt;/a&gt; (8min).&lt;/p&gt;

&lt;p&gt;#2. There’s one problem with the “get things done” attitude of startups: security. Here’s &lt;a href=&quot;https://alexschapiro.com/blog/security/vulnerability/2025/04/21/startups-need-to-take-security-seriously&quot;&gt;a breakdown of security issues found on a dating app&lt;/a&gt; (8min). They had broken one-time passwords and open endpoints. Security shouldn’t be an afterthought.&lt;/p&gt;

&lt;p&gt;#3. A URL shortener is maybe the new “hello, world” for distributed apps. But building one doesn’t have to be that complicated. Here’s &lt;a href=&quot;https://www.luu.io/posts/2025-over-engineer-url-shortener&quot;&gt;a simple not-overengineered alternative&lt;/a&gt; (6min).&lt;/p&gt;

&lt;p&gt;#4. Dragging and dropping controls onto a canvas is normal these days. Even the word “controls” is normal. But someone has to come up with all those ideas. They started with the release of Visual Basic, back in the 80s. Here’s &lt;a href=&quot;https://retool.com/visual-basic&quot;&gt;the history and legacy of Visual Basic&lt;/a&gt; (20min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;/2025/05/14/AlternativeToPostman/&quot;&gt;cURL, a simpler and faster alternative to Postman&lt;/a&gt; (2min) and &lt;a href=&quot;/2025/05/11/DigitsOnATree/&quot;&gt;how to find all 3-digit numbers in a tree&lt;/a&gt; (4min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by… Want to master SQL Server performance tuning? Brent Ozar, the leading SQL Server expert, is celebrating with a special anniversary sale.&lt;/p&gt;

&lt;p&gt;Only this month, get the &lt;a href=&quot;https://training.brentozar.com/p/recorded-class-season-pass-masters-classes?affcode=920087_fhe3khrq&quot;&gt;prerecorded Mastering bundle&lt;/a&gt; and save $400. This bundle includes four advanced courses covering indexes, queries, and full database optimization. Everything to optimize and tune your SQL server like a pro.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Ditch Your Helpers and Utilities Classes Before They Ruin Your Code</title>
   <link href="https://canro91.github.io/2025/05/15/Helpers/"/>
   <updated>2025-05-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/15/Helpers</id>
   <content type="html">&lt;p&gt;Recently, I needed to track down how an old Visual Basic WebForms app stored a value from the database into the Session object.&lt;/p&gt;

&lt;p&gt;But I didn’t know I was in trouble until I looked for the file in the GitHub repository and it said &lt;em&gt;“(Sorry about that, but we can’t show files that are this big right now.)”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The file had 69,359 lines. Its name? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GlobalFunctions.vb&lt;/code&gt;. You can guess what was in there just by looking at that name, right? It was a clear symptom of a deeper problem.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem?&lt;/h2&gt;

&lt;p&gt;The problem isn’t that it was a WebForms app in Visual Basic. We can write crappy code in any programming language. It was that huge “throw everything under the carpet” kind-of file.&lt;/p&gt;

&lt;p&gt;It reminded me of &lt;a href=&quot;/2022/12/07/BanningSomeNamingConventions/&quot;&gt;one of the conventions I decided to ban&lt;/a&gt;: “Helpers” and “Utility” classes, full of unrelated static methods.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The solution?&lt;/h2&gt;

&lt;p&gt;If you’re tempted to write anything named with “Helpers,” “Utilities,” or worse,  “GlobalFunctions,” please stop and consider how to split that class into smaller, more focused classes with clearer names. Smaller, well-named classes ease cognitive load, making them easier to work with.&lt;/p&gt;

&lt;p&gt;Or even better, if &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;you’re practicing DDD&lt;/a&gt;, before defaulting to a “Utility” class, ask yourself: does this belong inside an entity or &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;value object&lt;/a&gt; instead?&lt;/p&gt;

&lt;p&gt;Writing readable code, by ditching Utility classes, is just one of the skills every coder should master. But it takes more than typing symbols to become a senior coder.&lt;/p&gt;

&lt;p&gt;That’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; It’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=ditch-helpers-utilities-classes-they-ruin-your-code&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>cURL—A Simpler and Faster (and Free) Alternative to Postman</title>
   <link href="https://canro91.github.io/2025/05/14/AlternativeToPostman/"/>
   <updated>2025-05-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/14/AlternativeToPostman</id>
   <content type="html">&lt;p&gt;Postman still isn’t opening.&lt;/p&gt;

&lt;p&gt;Five minutes after clicking the icon… Nothing. Maybe it’s calling home, checking in with the International Space Station, or just ghosting me. Who knows?&lt;/p&gt;

&lt;p&gt;Today I found out about &lt;a href=&quot;https://www.usebruno.com/&quot;&gt;Bruno&lt;/a&gt;, a “Git-integrated, fully offline, and open source API client”. A simpler alternative to Postman. I had come across Bruno before but had forgotten about it.&lt;/p&gt;

&lt;h2 id=&quot;but-theres-an-even-simpler-alternative-to-postman-and-bruno&quot;&gt;But, there’s an even simpler alternative to Postman and Bruno.&lt;/h2&gt;

&lt;p&gt;When I want to test a single endpoint, I just:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Go to the Network tab on Chrome,&lt;/li&gt;
  &lt;li&gt;Right-click on the request I want to test,&lt;/li&gt;
  &lt;li&gt;Then click on “Copy as cURL (bash)”,&lt;/li&gt;
  &lt;li&gt;Then paste it into a Terminal, and&lt;/li&gt;
  &lt;li&gt;Press enter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://curl.se/&quot;&gt;cURL&lt;/a&gt; is a “command line tool and library for transferring data with URLs”. It comes preinstalled with Git Bash for Windows. And I’m a “terminal Git user,” so I keep a terminal open all the time. Way faster than opening an Electron app.&lt;/p&gt;

&lt;p&gt;Here are some cURL examples to send data:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;--json&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;name&quot;:&quot;some json here&quot;}&apos;&lt;/span&gt; https://example.com&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--json&lt;/span&gt; @body.json https://example.com
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--json&lt;/span&gt; @body.json &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;header-name:header-value&quot;&lt;/span&gt; https://example.com&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;data to PUT&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; PUT http://example.com/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the meaning of those options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--json&lt;/code&gt;: To POST a json request either as a json string or from a file.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-s&lt;/code&gt;: Silent mode&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i&lt;/code&gt;: To include response headers&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-k&lt;/code&gt;: To avoid validating SSL certificates. Handy when an ASP.NET Core project has HTTPS redirection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voilà! No waiting, no upgrades, no paywalled features. cURL is faster and simpler than Postman, which still doesn’t open on my computer. I guess it’s time to upgrade it or simply use a better alternative.&lt;/p&gt;

&lt;p&gt;PS: Turns out Postman finally opened… after an upgrade. Unnecessary drama. Arrggg!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Years Ago, I Was Sick and Burned Out. But This Powerful Quote Changed It All</title>
   <link href="https://canro91.github.io/2025/05/13/PrioritizeHealth/"/>
   <updated>2025-05-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/13/PrioritizeHealth</id>
   <content type="html">&lt;p&gt;In 2023, I felt sick to my stomach.&lt;/p&gt;

&lt;p&gt;By the end of the year, &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;I burned out&lt;/a&gt;. Then in 2024, &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;I was laid off&lt;/a&gt;. Perfect combination, right?&lt;/p&gt;

&lt;p&gt;For the first time, I had no idea what to do next.&lt;/p&gt;

&lt;p&gt;One day, I called a friend to tell him how I felt and get things off my chest. “You always have a plan,” he told me.&lt;/p&gt;

&lt;p&gt;But not that time. I only knew what I didn’t want to do. I didn’t want to go back to where I was: stomach sick, burned out, and without a job.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/12/21/MessySalary/&quot;&gt;I felt relieved after that layoff&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But it lasted only weeks before I realized I had no paycheck coming. I checked my expenses and bank account to see how long I could last without a paycheck. Relief quickly turned into desperation. More than once.&lt;/p&gt;

&lt;h2 id=&quot;the-quote-that-changed-everything&quot;&gt;The quote that changed everything&lt;/h2&gt;

&lt;p&gt;By chance or fate, I found this quote:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“If you don’t know what to do with your life, start by working on your health”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s from &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, one of my favorite writers. It’s from an interview or one of his books, I can’t remember exactly. I’ve been following his work online since then, by the way.&lt;/p&gt;

&lt;p&gt;Since last year, &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;after ditching my to-do lists&lt;/a&gt;, my only plan has been to take care of my body, mind, and spirit every day.&lt;/p&gt;

&lt;p&gt;Prioritizing our health creates forward momentum. It gives clarity, energy, and purpose.&lt;/p&gt;

&lt;p&gt;There are days when I miss working on any of the three parts.&lt;/p&gt;

&lt;p&gt;But when I miss a couple of days, I can feel it. Frustration, &lt;a href=&quot;/2024/12/19/TimeTravel/&quot;&gt;ruminating thoughts&lt;/a&gt;, and low energy start to appear. Again. That’s the warning sign to go back to my healthy habits. My body tells me when it needs to be taken care of.&lt;/p&gt;

&lt;p&gt;I still don’t know what to do with my life. Probably, I never will. But working on my health has given me so much peace of mind.&lt;/p&gt;

&lt;h2 id=&quot;one-day-at-a-time&quot;&gt;One day at a time&lt;/h2&gt;

&lt;p&gt;Life doesn’t come with an instruction manual. We have to figure things out. We all are figuring life out as we go. Sometimes it’s easy. Sometimes it’s not. Sometimes we know what we want out of life. Sometimes it’s easier to tell what we don’t want to do.&lt;/p&gt;

&lt;p&gt;No matter where you are in life, work on your health. That’s the first step to change.&lt;/p&gt;

&lt;p&gt;Focusing on my health was the lever that brought real change after burning out. That’s why I made it Idea #1 in &lt;a href=&quot;https://imcsarag.gumroad.com/l/10simpleideas/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=this-powerful-quote-changed-it&quot;&gt;10 Surprisingly Simple Ideas That Changed My Life And Could Change Yours Too&lt;/a&gt;. Because small daily actions create big change.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>4 Writing Lessons I&apos;ve Learned Recently—Two of Them After a Viral Post</title>
   <link href="https://canro91.github.io/2025/05/12/WritingLessons/"/>
   <updated>2025-05-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/12/WritingLessons</id>
   <content type="html">&lt;h2 id=&quot;1-use-fear-curiosity-and-desire-to-write-attention-grabbing-headlines&quot;&gt;#1. Use Fear, Curiosity, and Desire to write attention-grabbing headlines.&lt;/h2&gt;

&lt;p&gt;Credits to &lt;a href=&quot;https://ronwayjourney.medium.com/fewer-than-1-000-followers-heres-your-headline-guide-zero-views-to-100-views-5838d42ea982&quot;&gt;Ron Markley on Medium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently I’ve learned that when we write content, we’re in the business of &lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;writing headlines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A good headline is like a good welcome sign. People will only stop by if they see a good one.&lt;/p&gt;

&lt;p&gt;No matter how thoughtful our content is, if the headline doesn’t hook readers, they won’t stop to read it.&lt;/p&gt;

&lt;h2 id=&quot;2-when-writing-stories-or-fiction-a-point-in-time-creates-expectations-in-the-readers-minds&quot;&gt;#2. When writing stories or fiction, a point in time creates expectations in the readers’ minds.&lt;/h2&gt;

&lt;p&gt;When we write &lt;em&gt;“It was a sunny summer day when I saw her for the first time…“&lt;/em&gt; readers expect to hear more about what happened.&lt;/p&gt;

&lt;p&gt;And if we abruptly shift subjects, it’s like breaking readers’ expectations. Avoid those abrupt shifts. It’s like cutting off a conversation mid-way.&lt;/p&gt;

&lt;h2 id=&quot;3-listicles-attract-higher-engagement-on-social-media&quot;&gt;#3. Listicles attract higher engagement on social media.&lt;/h2&gt;

&lt;p&gt;Listicles work online and offline.&lt;/p&gt;

&lt;p&gt;The 10 Commandments, the 95 Theses by Martin Luther, the 48 Laws of Power… All of them are listicles.&lt;/p&gt;

&lt;p&gt;Since last year, I’ve been &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;experimenting on LinkedIn&lt;/a&gt;. And listicles have worked like a charm.&lt;/p&gt;

&lt;p&gt;My most viewed post was something like &lt;em&gt;“12 lessons after 10+ years in Software Engineering (In less than 1 minute)”&lt;/em&gt; followed by 10 one-liners and a question to invite people to the comment section. Boom! Over 80,000 views. I felt like an Internet celebrity for a couple of days… Here’s &lt;a href=&quot;https://www.linkedin.com/posts/iamcesaraguirre_12-lessons-after-10-years-in-software-engineering-activity-7320099047791288320-Q1jI?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAACymeHABx1YUIJc7QTfoAI_nEw6wLJ0btEU&quot;&gt;the post&lt;/a&gt;, if you’re on LinkedIn, by the way.&lt;/p&gt;

&lt;p&gt;When reading listicles, people tend to comment on the item that resonated the most.&lt;/p&gt;

&lt;h2 id=&quot;4-one-single-post-can-boost-your-follower-count&quot;&gt;#4. One single post can boost your follower count.&lt;/h2&gt;

&lt;p&gt;You know the Pareto principle, right? 80% of results come from 20% of effort.&lt;/p&gt;

&lt;p&gt;Well, 80% of followers and engagement come from 20% of posts. And one viral post can boost your follower count. It has happened to me on LinkedIn and Medium recently.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How To Find All 3-Digit Numbers In A Binary Tree</title>
   <link href="https://canro91.github.io/2025/05/11/DigitsOnATree/"/>
   <updated>2025-05-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/11/DigitsOnATree</id>
   <content type="html">&lt;p&gt;This is an exercise I failed to solve in a past interview a long time ago, in a galaxy far, far away. I ran out of time and didn’t finish it.&lt;/p&gt;

&lt;p&gt;Off the top of my head, the exercise was something like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Find all 3-digit numbers you can create by visiting a binary tree. To create a 3-digit number, given a node, visit either the left or right subtree, and concatenate the value you find in three nodes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next tree has 4 3-digit numbers.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        1
       / \
      /   \
     2     7
    / \   /
   5   9 4
  /
 3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;They are 125, 129, 253, and 174. No, 127 is not a valid number here. We should visit one subtree at a time.&lt;/p&gt;

&lt;p&gt;To finally close the open-loop in my mind, here’s my solution.&lt;/p&gt;

&lt;h2 id=&quot;lets-solve-the-simplest-scenario-first&quot;&gt;Let’s solve the simplest scenario first&lt;/h2&gt;

&lt;p&gt;If we have a tree with only the root node and the left subtree only with leaves, we could write a function to “walk” that simple tree like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; is the value of the root node and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subTree&lt;/code&gt; is either the left or right subtree of our root node.&lt;/p&gt;

&lt;p&gt;If we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Walk()&lt;/code&gt; with our example tree,&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        1
       / .
      /   .
     2     .
    / \   .
   5   9 .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The root is 1, the node in the left subtree is 2, and the node in the left subtree again is 5, then the 3-digit number is calculated as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1*100 + 2*10 + 5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Walk()&lt;/code&gt; passing only the root node and the left subtree of our sample tree returns,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;//        1&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//       / .&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//      /   .&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//     2     .&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//    / \   .&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//   5   9 .&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;Walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// List&amp;lt;int&amp;gt;(2) { 125, 129 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That will only work for a simple tree.&lt;/p&gt;

&lt;h2 id=&quot;lets-cover-more-complex-trees&quot;&gt;Let’s cover more complex trees&lt;/h2&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Walk()&lt;/code&gt;, we only find numbers by visiting one node in a simple tree, so let’s use recursion to visit all other nodes.&lt;/p&gt;

&lt;p&gt;Here’s a recursive function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Digits()&lt;/code&gt; that uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Walk()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
	
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;c1&quot;&gt;// ^^^&lt;/span&gt;
           &lt;span class=&quot;c1&quot;&gt;// We visit the left subtree from the root&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;//      ^^^^&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;// We visit the right subtree from the root&lt;/span&gt;
			  
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;// Find digits on the left subtree&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;// Find digits on the right subtree&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Digits()&lt;/code&gt; starts visiting the left and right subtrees from the root node. Then, it recursively calls itself for the left and right subtrees and concatenates all the intermediary lists.&lt;/p&gt;

&lt;h2 id=&quot;all-the-pieces-in-one-single-place&quot;&gt;All the pieces in one single place&lt;/h2&gt;

&lt;p&gt;Here’s my complete solution,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tree&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
	
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
			  
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Concat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aTree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tree1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;Digits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tree1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// List&amp;lt;int&amp;gt;(4) { 125, 129, 174, 253 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;I know it’s too late. Months have passed since then, but I wanted to solve it anyway. Don’t ask me what company asked me to solve that. I can’t remember. Wink, wink!&lt;/p&gt;

&lt;p&gt;For other interviewing exercises, check &lt;a href=&quot;/2019/08/02/PostfixNotationAnInterviewExercise/&quot;&gt;how to evaluate a postfix expression&lt;/a&gt;, &lt;a href=&quot;/2019/08/29/TimeComplexity/&quot;&gt;how to solve the two-sum problem&lt;/a&gt;, and &lt;a href=&quot;/2019/09/16/RotatingArray/&quot;&gt;how to shift the elements of an array&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Sell Something or Be Ready to Sell Your Time</title>
   <link href="https://canro91.github.io/2025/05/10/Selling/"/>
   <updated>2025-05-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/10/Selling</id>
   <content type="html">&lt;p&gt;Selling feels dirty to far too many people.&lt;/p&gt;

&lt;p&gt;Maybe that’s because we have the wrong impression of selling as tricking people into buying something they don’t need with pushy lines or sales tactics. “I’m making an exception for you. Hurry up! If my boss finds out about this, I’m fired.”&lt;/p&gt;

&lt;p&gt;The truth is we’re selling all the time, even without realizing it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/16/SpainTopCopywriter/&quot;&gt;Isra Bravo, the best copywriter of Spain&lt;/a&gt;, taught me that we’ve been selling since we’re kids. We sell ourselves to get our parents’ attention. And as adults, we continue selling: when looking for a job or for a romantic partner.&lt;/p&gt;

&lt;p&gt;The other day, &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;a salesy DM made me change my perspective on selling&lt;/a&gt;: from pushy sales tactics to genuine help.&lt;/p&gt;

&lt;p&gt;If we aren’t ashamed of helping, we shouldn’t be afraid of selling.&lt;/p&gt;

&lt;p&gt;These days, jobs are uncertain and &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;layoffs are always around the corner&lt;/a&gt;. Either you sell something (a book, course, template, coaching) and free yourself, or you sell 8 hours or more of your time and become a slave.&lt;/p&gt;

&lt;p&gt;Remember, “whoever sits in a cubicle is replaceable.” (Choose Yourself by &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;) Sell something or sell your time forever.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Put Blood in Your Opening Lines and Keep Your Readers Hooked Forever</title>
   <link href="https://canro91.github.io/2025/05/09/BleedInTheFirstLine/"/>
   <updated>2025-05-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/09/BleedInTheFirstLine</id>
   <content type="html">&lt;p&gt;People have short attention spans, and shorter for boring things.&lt;/p&gt;

&lt;p&gt;That’s why our job as writers is to keep readers moving from the first sentence to the next and to the next…&lt;/p&gt;

&lt;p&gt;I learned from &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, one of my favorite writers, to study the opening lines of the books I read to write my own.&lt;/p&gt;

&lt;p&gt;Following that advice, here are some of my favorite opening lines:&lt;/p&gt;

&lt;h2 id=&quot;genesis-by-moses&quot;&gt;Genesis by Moses&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;In the beginning, God created the heavens and the earth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the opening line of the Bible, Genesis 1:1. It doesn’t matter if you believe it or not, those 10 words hook you right from the start.&lt;/p&gt;

&lt;p&gt;If you read it for the first time and with fresh eyes, you can’t avoid asking:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Who is this God?&lt;/li&gt;
  &lt;li&gt;“In the beginning”…Was there anything before?&lt;/li&gt;
  &lt;li&gt;How did He do that?&lt;/li&gt;
  &lt;li&gt;And more important, why?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve been trying to answer that for years.&lt;/p&gt;

&lt;h2 id=&quot;one-hundred-years-of-solitude-by-gabriel-garcía-márquez&quot;&gt;One Hundred Years of Solitude by Gabriel García Márquez&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Many years later, as he faced the firing squad, Colonel Aureliano Buendía was to remember that distant afternoon when his father took him to discover ice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I didn’t read One Hundred Years of Solitude until the end. Sorry!&lt;/p&gt;

&lt;p&gt;That’s one of the required books in my Spanish classes. I was too young. Too many members of the Buendía family that I got lost.&lt;/p&gt;

&lt;p&gt;Even though I didn’t read it until the end, for some reason, I memorized that opening line in Spanish, its original language. It’s way stronger and more memorable in Spanish.&lt;/p&gt;

&lt;p&gt;“Many years later,” so it doesn’t start right at the beginning.&lt;/p&gt;

&lt;p&gt;“The firing squad,” what did this colonel do?&lt;/p&gt;

&lt;p&gt;Before getting shot, of all things, he remembers his father taking him to discover ice? Wait! Was ice that new? When is this happening?&lt;/p&gt;

&lt;h2 id=&quot;choose-yourself-by-james-altucher&quot;&gt;Choose Yourself by James Altucher&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;I was going to die.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Five words that hook you. You can’t avoid asking how and why.&lt;/p&gt;

&lt;p&gt;To add more drama, he continues:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I was going to die. The market had crashed. The Internet had crashed. Nobody would return my calls. I had no friends. Either I would have a heart attack or I would simply kill myself. I had a $4 million life insurance policy. I wanted my kids to have a good life. I figured the only way that could happen was if I killed myself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He answers the “why” by giving three causes: market, internet, and no friends. So is he an investor in internet companies? He doesn’t give a full answer to make us keep reading.&lt;/p&gt;

&lt;p&gt;This guy is so miserable that he wants to take his own life just to leave the insurance money to his kids. How did he end up there? You can’t avoid empathizing with him. You just want to keep reading.&lt;/p&gt;

&lt;h2 id=&quot;reinvent-yourself-by-james-altucher&quot;&gt;Reinvent Yourself by James Altucher&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;It was all over for me once again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wait! When was the last time? And why is that happening “again”? And what is “all”?&lt;/p&gt;

&lt;p&gt;To keep building up drama, he goes on:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It was all over for me once again. Marriage was over. My bank account going down. Nobody was publishing any more of my books. Nobody was giving me any more opportunities.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have read James Altucher’s previous books and heard his stories, you know he’s referring to one of the multiple times he went bankrupt. That explains the “again.”&lt;/p&gt;

&lt;p&gt;After that drama, you want to keep reading to know what he did to get back up. It’s a book about reinvention. He got back up, right?&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;Are there any patterns in those opening lines? Yes.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Thought-provoking or controversial statements like in Genesis.&lt;/li&gt;
  &lt;li&gt;Intriguing stories like in One Hundred Years of Solitude.&lt;/li&gt;
  &lt;li&gt;Rising drama and tension like in James Altucher’s books.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;James Altucher teaches about powerful opening lines. And he walks the talk in those two books by building up drama.&lt;/p&gt;

&lt;p&gt;A good headline is like a welcome sign. Controversy, curiosity, blood, and drama in the first line create the first impressions in your writing. Remember, you only have one chance to give a good first impression. So, put blood and drama in your opening lines and hook your readers until the end.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>4 Surprising Lessons I&apos;ve Learned Recently—While Taking a Loved One to a Hospital</title>
   <link href="https://canro91.github.io/2025/05/08/RandomLessons/"/>
   <updated>2025-05-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/08/RandomLessons</id>
   <content type="html">&lt;p&gt;During a recent hospital visit, I found inspiration to write.&lt;/p&gt;

&lt;p&gt;To keep my mind busy and productively distracted while waiting, I took out my phone and started to jot down ideas. Ideas of &lt;a href=&quot;/2025/04/19/CalmDown/&quot;&gt;how to calm my mind&lt;/a&gt; and ideas of &lt;a href=&quot;/2025/04/20/MedAI/&quot;&gt;how hospital visits might look in 2035&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From that visit, I’ve learned four surprisingly useful (and maybe random) things about health and well-being:&lt;/p&gt;

&lt;h2 id=&quot;1-insulin-helps-decrease-potassium-levels&quot;&gt;#1. Insulin helps decrease potassium levels.&lt;/h2&gt;

&lt;p&gt;When we hear insulin, we think of diabetes.&lt;/p&gt;

&lt;p&gt;But it turns out insulin helps the body absorb potassium faster. At least, that’s what a doctor explained to us. &lt;a href=&quot;/2025/04/02/KidneysDoMatter/&quot;&gt;My loved one has kidney failure&lt;/a&gt; and her potassium levels were outside the normal range.&lt;/p&gt;

&lt;h2 id=&quot;2-anemia-isnt-only-because-of-iron-deficiency&quot;&gt;#2. Anemia isn’t only because of iron deficiency.&lt;/h2&gt;

&lt;p&gt;It’s one of the causes, not the main one. It could be due to genetic reasons or red blood cells dying too soon or bone disorders… or excessive bleeding.&lt;/p&gt;

&lt;h2 id=&quot;3-too-much-exercise-before-bed-interrupts-your-sleep-patterns&quot;&gt;#3. Too much exercise before bed interrupts your sleep patterns.&lt;/h2&gt;

&lt;p&gt;I found this on &lt;a href=&quot;https://medicalxpress.com/news/2025-04-bed-linked-disrupted.html&quot;&gt;a study&lt;/a&gt; on the front page of Hacker News. I had to do something while waiting, so why not scroll down a feed?&lt;/p&gt;

&lt;p&gt;The study doesn’t discourage exercise. It compares light exercise to high-intensity exercise within 4 hours before bed.&lt;/p&gt;

&lt;p&gt;That won’t work as an excuse not to move your body. Sorry!&lt;/p&gt;

&lt;h2 id=&quot;4-when-rushing-life-makes-you-slow-down&quot;&gt;#4. When rushing, life makes you slow down.&lt;/h2&gt;

&lt;p&gt;In 2023, while finding ways to &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;recover from burnout&lt;/a&gt;, I read “The Ruthless Elimination of Hurry” by John Mark Comer.&lt;/p&gt;

&lt;p&gt;There was a line that has lived rent-free in my mind since then:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“God [or Nature or Universe] didn’t create hurry.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had to remember that recently. No matter how I hurry, I couldn’t change anything about what was outside my control.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>One Life Lesson That Took Me 10 Years to Learn</title>
   <link href="https://canro91.github.io/2025/05/07/LifeLesson/"/>
   <updated>2025-05-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/07/LifeLesson</id>
   <content type="html">&lt;p&gt;If you don’t come up with your own plan, society, the system, or the Matrix will give you its plan:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Work hard&lt;/li&gt;
  &lt;li&gt;Get 3% raises&lt;/li&gt;
  &lt;li&gt;Please your bosses&lt;/li&gt;
  &lt;li&gt;Keep your head down&lt;/li&gt;
  &lt;li&gt;Wait to retire&lt;/li&gt;
  &lt;li&gt;Then, die&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I only learned it after &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;being laid off&lt;/a&gt; and &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;recovering from burnout&lt;/a&gt; and stomach problems. That was my wake-up call.&lt;/p&gt;

&lt;p&gt;I was living on autopilot. No plan at all. Stretching each job until I got bored, fired, or laid off. I was working on building somebody else’s dream.&lt;/p&gt;

&lt;p&gt;I wish I had learned this lesson at 25, but at least I know it now.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Secret to a Sharp Mind from a 102-Year-Old Practicing Doctor</title>
   <link href="https://canro91.github.io/2025/05/06/SharpMind/"/>
   <updated>2025-05-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/06/SharpMind</id>
   <content type="html">&lt;p&gt;Dr. Howard Tucker, at 102, still works as a neurologist. He holds the Guinness World Record for the world’s oldest practicing doctor.&lt;/p&gt;

&lt;p&gt;Of course, the first question that popped into my mind was “What’s his secret?” I had to find out. That curiosity made me read &lt;a href=&quot;https://www.reddit.com/r/IAmA/comments/1jw22v5/im_dr_howard_tucker_a_102yearold_neurologist/&quot;&gt;his Reddit AMA&lt;/a&gt; and collect my favorite pieces of wisdom.&lt;/p&gt;

&lt;p&gt;Here they are:&lt;/p&gt;

&lt;h2 id=&quot;consistency-curiosity-moderation-and-a-good-sense-of-humor-havent-failed-me-yet&quot;&gt;“Consistency, curiosity, moderation, and a good sense of humor haven’t failed me yet”&lt;/h2&gt;

&lt;p&gt;I wasn’t the only one wondering about Dr. Tucker’s secret to his sharp mind. That question came up more than once throughout the whole discussion.&lt;/p&gt;

&lt;p&gt;It turns out there’s no secret or magic formula. Just good habits practiced consistently over the long term.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;… Never stop learning. I’ve always believed that the brain is like a muscle—if you don’t use it, it atrophies. I went to law school at 67 for the mental stimulation and because law fascinates me. Not because I needed another job, but because I wanted to challenge myself. You don’t need to necessarily pursue a degree—read, debate, and stay curious.&lt;/p&gt;

  &lt;p&gt;Keep working, if you can. I firmly believe that retirement is the enemy of longevity. I’m not saying everyone should work into their hundreds, but purpose matters. I still work in the medical field because it gives me structure, meaning, and a reason to wake up early (though I am enjoying sleeping in more now.) …&lt;/p&gt;

  &lt;p&gt;Move your body. I walk every day on my treadmill. It’s good for the brain and body, but hopefully you already knew that.&lt;/p&gt;

  &lt;p&gt;The truth is that there’s no secret sauce. I won’t deny genetics playing a large role in longevity. Genetics is a good head start, but consistency, curiosity, moderation, and a good sense of humor haven’t failed me yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a good reason to keep &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;following my Daily Practice&lt;/a&gt;: doing something for my body, mind, and spirit every day. Since last year, it’s been the one habit I’m following religiously. Maybe I’ll make it to 102 if I stick with it.&lt;/p&gt;

&lt;h2 id=&quot;your-job-doesnt-define-who-you-are-let-your-curiosity-and-how-you-treat-others-define-who-you-are-as-a-person&quot;&gt;“[Your job] doesn’t define who you are. Let your curiosity and how you treat others define who you are as a person”&lt;/h2&gt;

&lt;p&gt;A young medical intern asked for his advice on work-life balance during residency, and here’s what Dr. Tucker said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The key is to have at least one thing outside of medicine that is just yours, and you must commit to making time for it even if it’s just 15-30 minutes a day.&lt;/p&gt;

  &lt;p&gt;It can be anything - a hobby, a daily walk, reading, time with loved ones.&lt;/p&gt;

  &lt;p&gt;Always remember that medicine is what you do. It doesn’t define who you are. Let your curiosity and how you treat others define who you are as a person.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Replace “medicine” with your job, and that advice still applies.&lt;/p&gt;

&lt;p&gt;By the end of 2023, I was burned out. I wasn’t doing anything that Dr. Tucker recommends.&lt;/p&gt;

&lt;p&gt;I wasn’t practicing my hobbies. I wasn’t taking care of my body. I had stomach issues. Everything I ate sent me to the bathroom. Awful! And &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;I had all my sense of value in my job title&lt;/a&gt;. Wrong, wrong, wrong!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;I had to go through a burnout season&lt;/a&gt; to realize I am more than a job title and there’s more to life than working 8 hours in a virtual cubicle. I had to let go of the idea of climbing the corporate ladder. And I had to create my own rules for success and life.&lt;/p&gt;

&lt;p&gt;Since then, I redefined myself, from Software Engineer to lifelong learner. And I decided to &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;write 10 bad ideas&lt;/a&gt; and write something every single day as the things outside of coding.&lt;/p&gt;

&lt;h2 id=&quot;the-world-will-always-be-filled-with-uncertainty-and-challenging-times&quot;&gt;“The world will always be filled with uncertainty and challenging times”&lt;/h2&gt;

&lt;p&gt;One world war. The Great Depression. Another war. More than one economic crisis. In 102 years of life and almost 80 years of practice, Dr. Tucker has gone through a lot.&lt;/p&gt;

&lt;p&gt;His advice to young generations on making it through uncertain times?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;My advice is focus on what you can control - your effort, attitude, and how you treat others. The world will always be filled with uncertainty and challenging times, but resilience is timeless.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dr. Tucker is living proof that learning should never stop. And neither should we. Longevity isn’t about secrets. It’s about good habits that care for both mind and body. &lt;a href=&quot;/2025/01/18/WW2Veteran/&quot;&gt;Len, another 102-year-old WWII veteran, also recommends it&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Key Feature of Effective Writing in the Digital Age—Revealed by a Plea Against AI Misuse</title>
   <link href="https://canro91.github.io/2025/05/05/EffectiveWriting/"/>
   <updated>2025-05-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/05/EffectiveWriting</id>
   <content type="html">&lt;p&gt;Yesterday, while reading &lt;a href=&quot;https://claytonwramsey.com/blog/prompt/&quot;&gt;I’d rather read the prompt&lt;/a&gt; by Clayton Ramsey, with reasons why people use AI to write for them, I found this idea:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;When someone comments under a Reddit post with a computer-generated summary of the original text, I honestly believe that everyone in the world would be better off had they not done so. Either the article is so vapid that a summary provides all of its value, in which case, it does not merit the engagement of a comment, or it demands a real reading by a real human for comprehension, in which case the summary is pointless.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That made me think of a key feature for good writing on the Internet:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write something that doesn’t need a summary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I declared myself guilty. My first posts were word vomits, with long paragraphs, fancy words, and poor formatting. I wrote to &lt;a href=&quot;/2024/12/24/WritingVoice/&quot;&gt;sound like a “writer”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I blame school writing assignments: “Write a 5-page essay about…” And when we didn’t have anything to say, we started to go on tangents and add fluff to fill five pages.&lt;/p&gt;

&lt;p&gt;On the internet, there’s no word count to hit.&lt;/p&gt;

&lt;p&gt;Good writing is clear and to the point.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>If I Were Darth Vader&apos;s LinkedIn Ghostwriter and Brand Strategist:</title>
   <link href="https://canro91.github.io/2025/05/04/DarthVader/"/>
   <updated>2025-05-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/04/DarthVader</id>
   <content type="html">&lt;h2 id=&quot;tagline&quot;&gt;Tagline?&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Sith Lord — On a mission to rule the galaxy”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;about-page&quot;&gt;About page?&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Late projects and unmotivated team members?&lt;/p&gt;

  &lt;p&gt;I specialize in putting teams back on schedule.&lt;/p&gt;

  &lt;p&gt;My unconventional but proven methods will help you lead ambitious projects.&lt;/p&gt;

  &lt;p&gt;I found in the Dark Side a pathway to many abilities some consider to be unnatural.&lt;/p&gt;

  &lt;p&gt;Since then, I coach founders and executives on how to leverage the Dark Side too.&lt;/p&gt;

  &lt;p&gt;I teach them not to choke on their own aspirations.&lt;/p&gt;

  &lt;p&gt;DM “Dark Side” for a free 30-minute discovery call.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;hooks-for-the-first-10-posts&quot;&gt;Hooks for the first 10 posts?&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;How I found my best mentor&lt;/li&gt;
  &lt;li&gt;Here’s how to live with a ventilator&lt;/li&gt;
  &lt;li&gt;The #1 strategy I use to manage my teams&lt;/li&gt;
  &lt;li&gt;Why I joined the Dark side (and you should too)&lt;/li&gt;
  &lt;li&gt;1 lesson from failing at my most ambitious project&lt;/li&gt;
  &lt;li&gt;I had to choke one of my team members, here’s why&lt;/li&gt;
  &lt;li&gt;I failed to work with my son, here’s what I learned&lt;/li&gt;
  &lt;li&gt;Here’s what I love about being in the Dark Side&lt;/li&gt;
  &lt;li&gt;How I put projects back on schedule&lt;/li&gt;
  &lt;li&gt;Since I were a kid, I felt different&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;May the 4th be with you.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Hacks I&apos;ve Used To Learn Foreign Languages Faster—And Will Help You Too</title>
   <link href="https://canro91.github.io/2025/05/03/LanguageHacks/"/>
   <updated>2025-05-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/03/LanguageHacks</id>
   <content type="html">&lt;p&gt;Learning languages turned out to be the most valuable skill I’ve learned.&lt;/p&gt;

&lt;p&gt;It took me about 2 years to learn English and a couple more years to speak with confidence. I went to a traditional language school to get a diploma. Lots of grammar and “repeat after me” exercises.&lt;/p&gt;

&lt;p&gt;But I learned French, my second foreign language, in about a year and a half. I did it on my own with tutors and at my own pace.&lt;/p&gt;

&lt;p&gt;These are 10 tricks I’ve used (and recommend) to learn any language faster:&lt;/p&gt;

&lt;h2 id=&quot;1-forget-about-grammar-focus-on-your-pronunciation&quot;&gt;1. Forget about grammar. Focus on your pronunciation&lt;/h2&gt;

&lt;p&gt;When I started studying English, I was obsessed with grammar.&lt;/p&gt;

&lt;p&gt;On the first day at my language school, our teacher asked us to open a textbook to start learning rules and exceptions. Later, I devoured &lt;em&gt;Grammar in Use&lt;/em&gt;, a popular English textbook.&lt;/p&gt;

&lt;p&gt;But perfect grammar means nothing if you can’t get your points across. Work on your pronunciation first.&lt;/p&gt;

&lt;h2 id=&quot;2-learn-phrases-not-words&quot;&gt;2. Learn phrases, not words&lt;/h2&gt;

&lt;p&gt;It’s tempting to start memorizing individual words. A, aardvark, abacus, abandon, abase…&lt;/p&gt;

&lt;p&gt;Unless you want to sound like Tarzan, you need more than individual words. Start with simple phrases for casual conversations and combine them like Lego bricks.&lt;/p&gt;

&lt;h2 id=&quot;3-change-your-phone-language&quot;&gt;3. Change your phone language&lt;/h2&gt;

&lt;p&gt;That’s the easiest way to be immersed in your target language.&lt;/p&gt;

&lt;p&gt;We keep our phones with us all the time, even when going to bed. Turn them into language-learning companions.&lt;/p&gt;

&lt;h2 id=&quot;4-change-your-phones-alarm-sound-to-a-song&quot;&gt;4. Change your phone’s alarm sound to a song&lt;/h2&gt;

&lt;p&gt;Again, an easy way to immerse yourself in your target language. Make sure you wake up to do something you love, or you’ll hate that song.&lt;/p&gt;

&lt;h2 id=&quot;5-create-your-own-phrasebook&quot;&gt;5. Create your own phrasebook&lt;/h2&gt;

&lt;p&gt;List situations you’ll face when using your target language and the phrases you’ll need for them. Learning a language for your next vacation isn’t the same as learning it for a job interview. Use AI here as your tutor.&lt;/p&gt;

&lt;h2 id=&quot;6-create-flashcards-with-phrases-from-2-and-5&quot;&gt;6. Create flashcards with phrases from #2 and #5&lt;/h2&gt;

&lt;p&gt;Then, study them in every free slot in your agenda. Use spaced-repetition software like Anki for that.&lt;/p&gt;

&lt;h2 id=&quot;7-use-ai-to-generate-stories-with-the-most-common-words&quot;&gt;7. Use AI to generate stories with the most common words&lt;/h2&gt;

&lt;p&gt;Try this prompt:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Act as an expert tutor in &amp;lt;target language&amp;gt;, generate a 200-word story in the present tense about &amp;lt;favorite topic&amp;gt; using the 100 most common words.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then use a text-to-speech tool to generate audio for that story. Boom! Enough material to practice reading and listening.&lt;/p&gt;

&lt;p&gt;Again, remember to learn words in the context of phrases. That’s from #2.&lt;/p&gt;

&lt;h2 id=&quot;8-learn-filler-words-and-connectors-to-sound-like-a-native&quot;&gt;8. Learn filler words and connectors to sound like a native&lt;/h2&gt;

&lt;p&gt;Learn the “you know,” “and uh,” and “isn’t it?” to help your words flow naturally like a native speaker.&lt;/p&gt;

&lt;h2 id=&quot;9-watch-childrens-stories-multiple-times&quot;&gt;9. Watch children’s stories multiple times&lt;/h2&gt;

&lt;p&gt;Watch each time with a different focus:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The first time, watch to understand the story.&lt;/li&gt;
  &lt;li&gt;The second, with subtitles on, to read along.&lt;/li&gt;
  &lt;li&gt;The third, to identify words you don’t know.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, you can squeeze the most out of a single story.&lt;/p&gt;

&lt;p&gt;If you think children’s stories are too easy, try with a video from the YouTube channel “Easy Languages.”&lt;/p&gt;

&lt;h2 id=&quot;10-consume-content-about-your-hobbies-in-your-target-language&quot;&gt;10. Consume content about your hobbies in your target language&lt;/h2&gt;

&lt;p&gt;Okay, call me a nerd.&lt;/p&gt;

&lt;p&gt;But when I got back home from university, a long time ago in a galaxy far away, I watched lectures on YouTube about the same topics I had covered that day. I learned new words and practiced my listening skills while reinforcing the material I was covering.&lt;/p&gt;

&lt;p&gt;You don’t have to be a nerd like me. Replace university classes with hobbies or skills you want to learn. Win-win!&lt;/p&gt;

&lt;p&gt;I hope I have no grammar errors here. That would be embarrassing after saying I devoured &lt;em&gt;Grammar in Use.&lt;/em&gt; Anyway, &lt;a href=&quot;/2024/10/14/LearningLanguages/&quot;&gt;learning a language has been my best career move&lt;/a&gt; and &lt;a href=&quot;/2025/03/01/ReplacingGrammarly/&quot;&gt;an AI helped me proofread this&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Reason Why I&apos;ll Keep My Personal Blog (Even When Blogging Is Dead)</title>
   <link href="https://canro91.github.io/2025/05/02/Blogging/"/>
   <updated>2025-05-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/02/Blogging</id>
   <content type="html">&lt;p&gt;Blogging has been dead for years.&lt;/p&gt;

&lt;p&gt;Dead in the sense of writing posts to rank on search engines and attract visitors for ads. Google killed that style of blogging with constant algorithm updates. And ChatGPT seemed to kill Googling.&lt;/p&gt;

&lt;p&gt;But, personal blogs are still alive in some corners of the internet, like this one.&lt;/p&gt;

&lt;p&gt;I’m a romantic. I’ll keep writing on my blog, even if blogging is dead.&lt;/p&gt;

&lt;p&gt;My blog is &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;my time capsule&lt;/a&gt; and &lt;a href=&quot;/2025/01/27/TimesOfDeadBlogging/&quot;&gt;content vault&lt;/a&gt;. Everything that I write online ends up here. It’s my catch-all place. &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;It’s been better than a portfolio&lt;/a&gt; for my coding career.&lt;/p&gt;

&lt;p&gt;Each platform has its own set of unwritten rules. On LinkedIn, we’re expected to write about business-related topics. On Medium, we’re expected to write following publication guidelines. But a blog is a place without rules.&lt;/p&gt;

&lt;p&gt;Mike Saas said in &lt;a href=&quot;https://shellsharks.com/just-put-it-on-your-blog&quot;&gt;Just Put It on Your Blog&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;It’s great to have a place to share your thoughts. A place you can go back to when you want to remember something you had written or thought about before. A place you can refer people to when they have questions you’ve answered in the past. A place to be you.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My blog is my cabinet of curiosities, public notebook, and writing gym, away from social media’s speed. And that’s why I’ll keep blogging, even if the internet says it’s dead and even if &lt;a href=&quot;/2025/04/01/WhyIWillKeepWriting/&quot;&gt;AI writes faster and better one day&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>These 15 Simple Habits Could Save You From the Doctor&apos;s Office—For Good</title>
   <link href="https://canro91.github.io/2025/05/01/AvoidDoctors/"/>
   <updated>2025-05-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/05/01/AvoidDoctors</id>
   <content type="html">&lt;p&gt;Few things are more frustrating than going to the doctor.&lt;/p&gt;

&lt;p&gt;It isn’t just one visit. It’s one visit, followed by some blood tests at a laboratory, then going back to the doctor with the results, then a visit to a specialist, then another round of tests, then another visit with test results… And regular checkups. Arrggg!&lt;/p&gt;

&lt;p&gt;It’s draining just thinking about it. That’s why I’ve decided to adopt some healthy habits, like these:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Drink enough water to avoid kidney stones.&lt;/strong&gt; For so long, &lt;a href=&quot;/2025/04/02/KidneysDoMatter/&quot;&gt;I didn’t care about my kidneys&lt;/a&gt; until I had to rush to the ER with a loved one. Diagnosis? Kidney failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Cut sugar to avoid diabetes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Exercise to keep the heart healthy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Get a good night’s sleep.&lt;/strong&gt; It isn’t that complicated. I leave my phone outside my room, turn off all lights, change my bedding often, and regulate the AC temperature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Brush my teeth at least twice a day&lt;/strong&gt; and use dental floss once a day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Sit less to avoid back pain.&lt;/strong&gt; This was something that started in my first job. I wasn’t used to sitting for 8 hours on an uncomfortable chair. I had to change my chair at work and start walking. And more recently, I started to use a standing desk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Eat vegetables with every meal&lt;/strong&gt; to go to the bathroom every day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Place my computer near a window for natural light&lt;/strong&gt;, and put a lamp behind my display so it isn’t the brightest source of light at night.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Cut processed food and sodas&lt;/strong&gt; to keep my blood pressure under control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Have a daily moment of silence&lt;/strong&gt; to control stress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#11. Wear a good pair of shoes.&lt;/strong&gt; Again, back pain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#12. Eat more protein than carbs at every meal&lt;/strong&gt; to keep my weight under control. Not only eat more protein, but also reorder how you eat. Salads first, then proteins, and lastly carbs. Thanks to reordering how I eat, I feel more energetic throughout the day. And that’s &lt;a href=&quot;/2024/12/30/BestProductivityHack/&quot;&gt;my best productivity hack&lt;/a&gt; since last year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#13. Stay in contact with friends.&lt;/strong&gt; Just to be happier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#14. Take supplements for brain health.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#15. Don’t drink alcohol&lt;/strong&gt; to protect the liver.&lt;/p&gt;

&lt;p&gt;Maybe one day, &lt;a href=&quot;/2025/04/20/MedAI/&quot;&gt;AI will transform our visits to hospitals and doctors&lt;/a&gt;. Who knows? But until then, I want to be healthy to avoid those long appointments.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>15 Writing Myths Debunked So You Can Start Writing Today</title>
   <link href="https://canro91.github.io/2025/04/30/WritingMyths/"/>
   <updated>2025-04-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/30/WritingMyths</id>
   <content type="html">&lt;p&gt;In 2024, I went all in with my writing.&lt;/p&gt;

&lt;p&gt;Writing worked like free therapy to recover from burnout. After months of inactivity, I decided to revive my LinkedIn account. I started writing 1 post a week, and of course nothing happened. Then I wrote 2 posts…then 3…until I settled on writing every workday.&lt;/p&gt;

&lt;p&gt;Some of my ex-coworkers and friends have noticed some of my posts. They have genuinely asked me how to do it on their own.&lt;/p&gt;

&lt;p&gt;Here are some of the myths about writing anywhere online I’ve heard:&lt;/p&gt;

&lt;h2 id=&quot;1-im-not-an-expert&quot;&gt;1. I’m not an expert&lt;/h2&gt;

&lt;p&gt;Who’s an expert anyway? If you wait to be an expert, you will never start writing.&lt;/p&gt;

&lt;p&gt;It takes 10,000 hours to be an expert. Do you have time for that? Write to be an expert, don’t wait to be one to start.&lt;/p&gt;

&lt;h2 id=&quot;2-i-dont-know-how-to-write&quot;&gt;2. I don’t know how to write&lt;/h2&gt;

&lt;p&gt;Writing takes time to master. But to start, &lt;a href=&quot;/2024/12/24/WritingVoice/&quot;&gt;imagine writing for one person&lt;/a&gt;: a friend, your kid, a coworker, or your dog. Talk out loud and transcribe it. Or imagine you’re texting a friend and &lt;a href=&quot;/2025/03/05/WritingVoice/&quot;&gt;write inside a chat app&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-i-dont-want-to-expose-myself&quot;&gt;3. I don’t want to expose myself&lt;/h2&gt;

&lt;p&gt;You don’t have to share pictures of your feet. Don’t write about subjects you wouldn’t bring up at work.&lt;/p&gt;

&lt;h2 id=&quot;4-im-not-a-native-english-speaker&quot;&gt;4. I’m not a native English speaker&lt;/h2&gt;

&lt;p&gt;Write in your native language.&lt;/p&gt;

&lt;h2 id=&quot;5-im-not-good-at-explaining-things&quot;&gt;5. I’m not good at explaining things&lt;/h2&gt;

&lt;p&gt;If you’re a coder, you are already good at explaining things. Coding is explaining things to a computer. Try to do the same in writing. Don’t try to write like a “writer,” share an algorithm. &lt;a href=&quot;/2025/04/10/TilPosts/&quot;&gt;Start by writing TIL Posts&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;6-i-dont-have-anything-to-share&quot;&gt;6. I don’t have anything to share&lt;/h2&gt;

&lt;p&gt;If you have learned something in the past 2 years, you have something to share. And &lt;a href=&quot;/2025/01/26/ContentIsEverywhere/&quot;&gt;you’re already sitting on content ideas&lt;/a&gt;, you just need to notice them.&lt;/p&gt;

&lt;h2 id=&quot;7-i-dont-know-where-to-publish&quot;&gt;7. I don’t know where to publish&lt;/h2&gt;

&lt;p&gt;Start on social media. Start writing a tweet or whatever they’re called now. Or &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;try with LinkedIn&lt;/a&gt;. It might be cringy, but it has fewer trolls. On LinkedIn, the feeling is the boss is watching, so everybody behaves.&lt;/p&gt;

&lt;h2 id=&quot;8-my-boss-will-find-out&quot;&gt;8. My boss will find out&lt;/h2&gt;

&lt;p&gt;Your boss won’t care. Your boss is busy pleasing their own bosses.&lt;/p&gt;

&lt;h2 id=&quot;9-i-need-my-bosss-permission&quot;&gt;9. I need my boss’s permission&lt;/h2&gt;

&lt;p&gt;Unless you’re planning to write about company secrets, you don’t need your boss’s permission. You don’t need permission from anyone.&lt;/p&gt;

&lt;p&gt;In fact, &lt;a href=&quot;/2024/12/06/AlwaysWriting/&quot;&gt;always write about what you do at work&lt;/a&gt;. That’s better than claiming you did something on your CV.&lt;/p&gt;

&lt;h2 id=&quot;10-people-will-make-fun-of-me&quot;&gt;10. People will make fun of me&lt;/h2&gt;

&lt;p&gt;Nobody will make fun of you. Just hit publish.&lt;/p&gt;

&lt;h2 id=&quot;11-nobody-will-read&quot;&gt;11. Nobody will read&lt;/h2&gt;

&lt;p&gt;You can always read and like your own stuff on social media. Write for your past self, and don’t worry if anyone reads.&lt;/p&gt;

&lt;h2 id=&quot;12-what-if-my-writing-comes-up-in-future-interviews&quot;&gt;12. What if my writing comes up in future interviews&lt;/h2&gt;

&lt;p&gt;Congrats! It means someone found and read your stuff. And it will make you memorable. “Oh, the guy who wrote about XYZ.”&lt;/p&gt;

&lt;p&gt;By writing, you can skip hiring lines. For example, &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;my blog has done more for me than a portfolio&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;13-i-have-too-many-ideas-i-cant-decide-what-to-post&quot;&gt;13. I have too many ideas. I can’t decide what to post.&lt;/h2&gt;

&lt;p&gt;Post what you would have liked to read 2 years ago. See what sticks and keep writing about that.&lt;/p&gt;

&lt;h2 id=&quot;14-i-sit-to-write-but-nothing-comes-up&quot;&gt;14. I sit to write but nothing comes up&lt;/h2&gt;

&lt;p&gt;OK, that’s writer’s block. But &lt;a href=&quot;/2025/02/16/ForgetWritersBlock/&quot;&gt;that’s not the real problem&lt;/a&gt;. Again, write for your past self. Or &lt;a href=&quot;/2025/02/22/WritingPrompts/&quot;&gt;try following writing prompts&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;15-i-dont-know-how-to-create-a-website&quot;&gt;15. I don’t know how to create a website&lt;/h2&gt;

&lt;p&gt;You don’t need a website. Start on social media or social blogs. And if you’re a coder and want to start writing, &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;don’t code a blogging engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don’t have a New York Times best-seller and I’m not an expert either and you made it this far. That’s the power of writing online. Write as if nobody is reading and keep writing because you don’t know who you might help. Start today and see where your words take you.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ten Pointless Facts About Me Challenge—Here We Go!</title>
   <link href="https://canro91.github.io/2025/04/29/TenPointlessFacts/"/>
   <updated>2025-04-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/29/TenPointlessFacts</id>
   <content type="html">&lt;p&gt;While scrolling through yesterday’s Minifeed for inspiration, I found this blogging challenge on &lt;a href=&quot;https://louplummer.lol/forking-mad/&quot;&gt;Living Out Loud&lt;/a&gt;. Turns out &lt;a href=&quot;https://forkingmad.blog/ten-pointless-facts-about-me/&quot;&gt;David, from Forking Mad +&lt;/a&gt;, started it. No more writer’s block.&lt;/p&gt;

&lt;p&gt;Here I go:&lt;/p&gt;

&lt;h2 id=&quot;do-you-floss-your-teeth&quot;&gt;Do you floss your teeth?&lt;/h2&gt;

&lt;p&gt;Yes. Once a day.&lt;/p&gt;

&lt;h2 id=&quot;tea-coffee-or-water&quot;&gt;Tea, coffee, or water?&lt;/h2&gt;

&lt;p&gt;Coffee only in the mornings with breakfast. No sugar. 1L of water a day. And tea, occasionally after dinner.&lt;/p&gt;

&lt;h2 id=&quot;footwear-preference&quot;&gt;Footwear preference?&lt;/h2&gt;

&lt;p&gt;Nothing in particular. But I like to &lt;a href=&quot;/2025/03/09/TravelTips/&quot;&gt;wear shoes without laces when traveling&lt;/a&gt;. It saves so much time at security controls.&lt;/p&gt;

&lt;h2 id=&quot;favorite-dessert&quot;&gt;Favorite dessert?&lt;/h2&gt;

&lt;p&gt;I don’t have a sweet tooth. But if you’re curious, &lt;a href=&quot;https://www.mycolombianrecipes.com/arroz-con-leche-rice-pudding/&quot;&gt;Arroz con Leche&lt;/a&gt; (Rice pudding. Literally, rice with milk) is a popular dessert in my country.&lt;/p&gt;

&lt;h2 id=&quot;the-first-thing-you-do-when-you-wake-up&quot;&gt;The first thing you do when you wake up?&lt;/h2&gt;

&lt;p&gt;Brush my teeth, a glass of water, and a moment of silence. I started the last two, as part of my Daily Practice, after &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;ditching my todo lists&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;age-youd-like-to-stick-at&quot;&gt;Age you’d like to stick at?&lt;/h2&gt;

&lt;p&gt;I wouldn’t like to stick at any age. Every age comes with challenges to face and lessons to learn. Last year, &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;I got laid off and recovered from burnout&lt;/a&gt; and I learned some valuable lessons. That was something I wouldn’t have learned at any other age.&lt;/p&gt;

&lt;h2 id=&quot;how-many-hats-do-you-own&quot;&gt;How many hats do you own?&lt;/h2&gt;

&lt;p&gt;Not exactly hats, but I have a couple of baseball caps.&lt;/p&gt;

&lt;p&gt;One day, I attended a meeting on Teams at work in a past job. And during a 1-on-1 with the VP of Engineering, he said, “If you want to be a team leader, you should start looking like one.” And that meant stopping wearing caps. Leadership required a different look. Arrggg!&lt;/p&gt;

&lt;h2 id=&quot;describe-the-last-photo-you-took&quot;&gt;Describe the last photo you took?&lt;/h2&gt;

&lt;p&gt;I took a photo of a loved one coming back home after almost a week in a hospital.&lt;/p&gt;

&lt;h2 id=&quot;worst-tv-show&quot;&gt;Worst TV show?&lt;/h2&gt;

&lt;p&gt;I don’t have a TV show I’d call “the worst.”&lt;/p&gt;

&lt;p&gt;Maybe it’s because I don’t watch that many TV shows. But I will tell you some of my favorite ones: Homeland, The Diplomat, &lt;a href=&quot;/2024/12/09/Scorpion/&quot;&gt;Scorpion&lt;/a&gt;, and Dr. House.&lt;/p&gt;

&lt;p&gt;I used to feel guilty about watching TV shows. But I realized when we see the world with our writer’s glasses on, &lt;a href=&quot;/2025/01/26/ContentIsEverywhere/&quot;&gt;content ideas are everywhere&lt;/a&gt;. So I started writing about TV shows and movies I watch. No more feeling guilty.&lt;/p&gt;

&lt;h2 id=&quot;as-a-child-what-was-your-aspiration-for-adulthood&quot;&gt;As a child, what was your aspiration for adulthood?&lt;/h2&gt;

&lt;p&gt;I had many ideas: joining the Navy, studying Biology, being a polyglot, and many others I can’t even recall. &lt;a href=&quot;/2024/12/16/FindYourPassion/&quot;&gt;I’ve always struggled to find one passion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also answered the &lt;a href=&quot;/2025/01/13/BlogQuestionsChallenge/&quot;&gt;Bear Blog Questions Challenge&lt;/a&gt; the other day.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>8 Easy-to-Implement Tips to Read One Book a Week</title>
   <link href="https://canro91.github.io/2025/04/28/OneBookAWeek/"/>
   <updated>2025-04-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/28/OneBookAWeek</id>
   <content type="html">&lt;p&gt;I’ve changed my mind about reading books.&lt;/p&gt;

&lt;p&gt;I started reading to grow a large “read list.” But I couldn’t remember some of those books, even when I had notes. I couldn’t even remember their covers.&lt;/p&gt;

&lt;p&gt;Then, I switched to “just-in-time” learning. Reading only when facing a challenge or working a project, so I didn’t forget the lessons from books.&lt;/p&gt;

&lt;p&gt;Reading one book a week seemed like a distant goal. &lt;a href=&quot;/2024/11/20/Read500Books/&quot;&gt;Reading 500 books to reinvent myself&lt;/a&gt; seemed impossible. I thought it was only doable with speed-reading techniques and tweaking the definition of a “read” book.&lt;/p&gt;

&lt;p&gt;But yesterday, I found this YouTube video with practical ideas to read one book a week.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/VjJlHqM9p34?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Here are some of my takeaways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. You do have time to read.&lt;/strong&gt; On average, we spend three hours watching Netflix. That’s time we could use for reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. You don’t have to read in a single 3-hour session.&lt;/strong&gt; Split your reading time throughout the day. Read during idle moments: while taking your morning coffee, after lunch, or before going to bed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Have dedicated spaces for reading.&lt;/strong&gt; Make sure they’re comfortable enough. Maybe a good chair and a source of natural light or a lamp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Add 20 seconds of friction to your distractions.&lt;/strong&gt; It could mean leaving your TV remote in another room or &lt;a href=&quot;/2024/12/13/KeepingPhonesAround/&quot;&gt;taking your phone out of sight&lt;/a&gt;. Also, make your reading sessions as easy as possible to start. Make sure starting a reading session takes you just 20 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. You don’t have to finish one book to start another.&lt;/strong&gt; Balance your reading among three books: one for learning, one for entertainment, and one for inspiration. I found a similar idea on Choose Yourself by &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;: Read for two hours and &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;write down 10 ideas&lt;/a&gt; from what you read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Every time you find interesting concepts or ideas, pause for a moment.&lt;/strong&gt; This is to give your brain time to activate its diffuse mode and start working behind the scenes. Also, notice connections between what you’re reading and the other two books. Maybe a concept from a non-fiction book might appear applied in an autobiography. This sounds similar to step #6 from &lt;a href=&quot;/2024/05/13/HowToReadNonFictionBooks/&quot;&gt;my 6-step process to read books&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7.&lt;/strong&gt; When you find a new concept, ask yourself &lt;strong&gt;what’s the easiest step to start implementing that concept.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Have a book implementation journal.&lt;/strong&gt; Every time you apply a new concept you found in a book, keep a log entry of how you did and how easy it was to apply.&lt;/p&gt;

&lt;p&gt;More ideas for &lt;a href=&quot;/2025/01/09/ReadingStrategy/&quot;&gt;my own reading strategy&lt;/a&gt;. So &lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;always be reading&lt;/a&gt;, not to show off a large book list, but to retain and apply.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>I Followed These 7 Proven Money Tips—And Finally Got My Finances Under Control</title>
   <link href="https://canro91.github.io/2025/04/27/FinancesInOrder/"/>
   <updated>2025-04-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/27/FinancesInOrder</id>
   <content type="html">&lt;p&gt;The fear of letting my salary slip through my fingers pushed me to organize my finances.&lt;/p&gt;

&lt;p&gt;I had doubled my salary as a Software Engineer. I was earning in a stronger currency. And I didn’t want to regret letting all that money leave my pockets without doing anything productive with it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/05/MoneyBooks/&quot;&gt;I read finance books&lt;/a&gt; and watched YouTube influencers to get my finances in order.&lt;/p&gt;

&lt;p&gt;Out of all that content, here are 7 tips I followed to take care of my finances:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. List your sources of income and expenses.&lt;/strong&gt; This is to know how your money is flowing in and out of your pockets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Follow the 50/30/20 rule for conscious spending instead of strict budgeting.&lt;/strong&gt; I learned this one from “I Will Teach You To Be Rich” by Ramit Sethi. Set apart 50% of your income for fixed expenses like housing and food, 30% for savings and investing, and 20% for guilt-free spending. Of course, tweak those last 30% and 20% to suit your needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use the envelope method.&lt;/strong&gt; Separate your income into categories by putting your money into envelopes. And only spend what you have in each envelope for each category. For example, only use the money from the “Coffee” envelope to drink coffee outside. Of course, you can follow this method with virtual envelopes in your bank account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Pay yourself first.&lt;/strong&gt; This is a lesson from “The Richest Man in Babylon.” Before paying for your expenses, save and invest first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Start an emergency fund.&lt;/strong&gt; Call it F*ck-you money or whatever you want. But save enough to cover months of expenses in case you lose your job or face any other emergency. A strong emergency fund was crucial when &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;layoffs came knocking&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Be careful with your credit card.&lt;/strong&gt; Use it if wisely. Only if you already have money to pay for purchases and take advantage of extra benefits like travel insurance or access to lounge rooms in airports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Automate your payments and investments.&lt;/strong&gt; Again, another lesson from “I Will Teach You To Be Rich.” The goal is to live a rich life, outside of spreadsheets. So automate payments for your credit card and automate deposits into your savings or brokerage accounts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Steal the Headlines from Some of My Most-Read Posts</title>
   <link href="https://canro91.github.io/2025/04/26/MyMostReadHeadlines/"/>
   <updated>2025-04-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/26/MyMostReadHeadlines</id>
   <content type="html">&lt;p&gt;Your headline is the front door to your content.&lt;/p&gt;

&lt;p&gt;People will only enter your shop after seeing an attention-grabbing sign on the front door. Headlines work the same. No matter how thoughtful and actionable your post is, if the headline doesn’t “hook” them, they won’t read it.&lt;/p&gt;

&lt;p&gt;To write better headlines, &lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;steal them like an artist from YouTube&lt;/a&gt; or from other posts.&lt;/p&gt;

&lt;p&gt;Here are the headline templates from some of my most-read posts on Medium. Steal them and tweak them to fit your content.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;&amp;lt;Number&amp;gt; Subjects I’ve Changed My Mind About as a &amp;lt;Profession/Role&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;9 subjects I’ve changed my mind about as a Software Engineer&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;For &amp;lt;Outcome&amp;gt;, &amp;lt;Key Action&amp;gt; in Your &amp;lt;Context&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;For cleaner domains, move IO to the edges of your app&lt;/li&gt;
      &lt;li&gt;To break free from debt, stop using your credit card&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Using &amp;lt;Popular Action&amp;gt; Doesn’t Make Your &amp;lt;Context&amp;gt; Truly &amp;lt;Desired Outcome&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Using lambda expressions doesn’t make your C# code functional&lt;/li&gt;
      &lt;li&gt;Cutting down on cups of coffee doesn’t make you save more money&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&amp;lt;Number&amp;gt; Alternatives for &amp;lt;Common Action&amp;gt; When &amp;lt;Context&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Three alternatives for the startup class when migrating ASP.NET Core projects&lt;/li&gt;
      &lt;li&gt;Five alternatives for eating out when saving money for your next trip abroad&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The Best Way to Get Better at &amp;lt;Skill&amp;gt; Isn’t Just &amp;lt;Common Misconception&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;The best way to get better at coding isn’t just writing more code&lt;/li&gt;
      &lt;li&gt;The best way to make more money isn’t just asking for a raise&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;If You Enjoy &amp;lt;Activity&amp;gt;, Think Twice About &amp;lt;Counterintuitive Decision&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;If you enjoy coding, think twice about joining the management track&lt;/li&gt;
      &lt;li&gt;If you enjoy photography, think twice about starting a wedding photography business&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;You Only Need These &amp;lt;Number&amp;gt; &amp;lt;Category&amp;gt; to &amp;lt;Big Transformation&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;You only need these four books to change your relationship with money&lt;/li&gt;
      &lt;li&gt;You only need these two gym routines to lose 10KG of fat in the next 30 days&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Steal This &amp;lt;Number&amp;gt;-Step &amp;lt;Process&amp;gt; to &amp;lt;Outcome&amp;gt;&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;Steal this 6-step reading process to retain more from books&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;Steal this 5-step process to land your first coding job&lt;/em&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As writers, our job is to hook readers one line at a time. Nail your headlines and opening lines and you’ll &lt;a href=&quot;/2025/03/11/BloggingTips/&quot;&gt;write posts readers can’t stop reading&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Re: Will AI Take My Job? A Coder&apos;s Reality Check</title>
   <link href="https://canro91.github.io/2025/04/25/WillAITakeMyJob/"/>
   <updated>2025-04-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/25/WillAITakeMyJob</id>
   <content type="html">&lt;p&gt;Anita asked on &lt;a href=&quot;https://dev.to/anitaolsen/will-ai-take-my-job-1ap0&quot;&gt;dev.to&lt;/a&gt; if AI, given all the hype, will take her job.&lt;/p&gt;

&lt;p&gt;Short answer: Not yet.&lt;/p&gt;

&lt;p&gt;Sure, we code daily and AI shines at spitting out code. But most of our work is balancing expectations and handling risk.&lt;/p&gt;

&lt;p&gt;Apart from coding, we as coders have to deal with:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Inter-team planning&lt;/li&gt;
  &lt;li&gt;Brainstorming sessions&lt;/li&gt;
  &lt;li&gt;Putting late sprints back on track&lt;/li&gt;
  &lt;li&gt;Designing requirements and user stories&lt;/li&gt;
  &lt;li&gt;Decomposing a full project into milestones&lt;/li&gt;
  &lt;li&gt;Scoping tasks with Product people&lt;/li&gt;
  &lt;li&gt;Reviewing architecture designs&lt;/li&gt;
  &lt;li&gt;Negotiating deadlines&lt;/li&gt;
  &lt;li&gt;Talking to clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that involves a lot of human interaction. It shouldn’t surprise anyone that a coder will spend more time in meetings than coding on a normal day. And AI can’t replace that human interaction yet.&lt;/p&gt;

&lt;p&gt;But sure, &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;our job as coders will change&lt;/a&gt;. Even &lt;a href=&quot;/2025/02/24/AVeteranOnAI/&quot;&gt;if we’re skeptical about AI&lt;/a&gt;, we can’t ignore it. We can only assume AI will generate code faster and cheaper than any of us. &lt;a href=&quot;/2025/01/21/AIAdaptation/&quot;&gt;We have to adapt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We won’t be code monkeys anymore, cracking lines of code in exchange for bananas. AI will handle that. And if AI will change everything, let it at least kill dumb SCRUM ceremonies.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>You Don&apos;t Need Markdown to Blog—But It Makes It Easier</title>
   <link href="https://canro91.github.io/2025/04/24/BloggingAndMarkdown/"/>
   <updated>2025-04-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/24/BloggingAndMarkdown</id>
   <content type="html">&lt;p&gt;These days, Ben, one of &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;my email subscribers&lt;/a&gt;, asked me a question about blogging using Markdown.&lt;/p&gt;

&lt;p&gt;Here’s an edited version of his email:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I have been on a journey to start a coding blog over the past couple of months but just cannot get behind Markdown blogging in an IDE, which seems to be the most common or popular way to create a blog. I find it far easier to use some web service that essentially amounts to a rich text editor.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;What would you recommend in this instance? Am I missing some obvious solutions or is getting the hang of Markdown just the way everyone recommends doing this?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, I’m a &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;plain-text fan&lt;/a&gt;. Writing posts using Markdown on a text editor is my favorite way to blog.&lt;/p&gt;

&lt;h2 id=&quot;do-you-have-to-use-markdown-short-answer-no&quot;&gt;Do you have to use Markdown? Short answer: No.&lt;/h2&gt;

&lt;p&gt;You could write HTML files and publish them directly to the Internet.&lt;/p&gt;

&lt;p&gt;I’ve even seen people blogging using GitHub Gist or public GitHub repositories. They simply share file URLs from the repo.&lt;/p&gt;

&lt;p&gt;A Markdown-based blogging engine like Jekyll is convenient. You could try editors like &lt;a href=&quot;https://macdown.uranusjr.com/&quot;&gt;MacDown&lt;/a&gt; or &lt;a href=&quot;https://typora.io/&quot;&gt;Typora&lt;/a&gt;. These days, I’m using &lt;a href=&quot;https://notable.app/&quot;&gt;Notable&lt;/a&gt;. Or you could try a Markdown extension for Visual Studio Code.&lt;/p&gt;

&lt;p&gt;And by the way, yesterday, I found &lt;a href=&quot;https://ingau.me/blog/how-i-write-my-blogs-in-obsidian-and-publish-instantly/&quot;&gt;a guy who runs his blog with Obsidian&lt;/a&gt;, if you’re looking for inspiration.&lt;/p&gt;

&lt;h2 id=&quot;now-if-youre-writing-for-the-first-time-on-the-internet-id-recommend-to-start-on-a-social-blog&quot;&gt;Now, if you’re writing for the first time on the Internet, I’d recommend to start on a “social blog.”&lt;/h2&gt;

&lt;p&gt;A social blog is a place for long-form writing with an audience and a distribution mechanism, like dev.to or Medium.&lt;/p&gt;

&lt;p&gt;Social blogs are “slower” than social media platforms like Twitter/X or LinkedIn, but “faster” than traditional blogs or websites. It’s easier to get traction on a social blog than on a personal blog, which sits behind search engines and their bots.&lt;/p&gt;

&lt;p&gt;I’d recommend starting on dev.to.&lt;/p&gt;

&lt;p&gt;I have an account there where I repost some of my coding content. It has a decent built-in editor with basic formatting, but still uses Markdown.&lt;/p&gt;

&lt;p&gt;It has a large audience of mostly beginner coders learning web development. Of course, you can share any content related to coding there.&lt;/p&gt;

&lt;p&gt;You have nothing to lose by starting a blog. Start writing, even if it’s just one post. Choose the simplest option so you can focus on writing, not tweaking tools or writing your own blogging engine.&lt;/p&gt;

&lt;p&gt;For more blogging lessons, read &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;four lessons for a coder struggling to write&lt;/a&gt;, &lt;a href=&quot;/2025/04/10/TilPosts/&quot;&gt;start writing by writing TIL posts&lt;/a&gt;, and &lt;a href=&quot;/2024/12/15/BloggingWorkflow/&quot;&gt;how I organize my blogging workflow&lt;/a&gt; as inspiration.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Reading Is a Cheat Code to Better Writing</title>
   <link href="https://canro91.github.io/2025/04/23/Reading/"/>
   <updated>2025-04-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/23/Reading</id>
   <content type="html">&lt;p&gt;Last year, I challenged myself to revive my LinkedIn account by writing 100 posts.&lt;/p&gt;

&lt;p&gt;I didn’t know what to write or how to write it. I only had good intentions.&lt;/p&gt;

&lt;p&gt;My first posts were crap. Of course, &lt;a href=&quot;/2025/01/02/SturgeonLaw/&quot;&gt;90% of everything is crap&lt;/a&gt;. I made every sin possible in my first posts: emojis, a big wall of text, external links…I’m embarrassed by those first posts.&lt;/p&gt;

&lt;h2 id=&quot;but-reading-as-a-creator-changed-my-writing&quot;&gt;But reading as a creator changed my writing.&lt;/h2&gt;

&lt;p&gt;I started to notice the posts I opened and read.&lt;/p&gt;

&lt;p&gt;I collected the opening lines of the posts I opened. I created a file with my favorite openers. Then I stole (like an artist) those hooks to write my own.&lt;/p&gt;

&lt;p&gt;Every time I clicked “See more” and found a big wall of text, I stopped reading. To avoid this mistake, I started to write shorter posts.&lt;/p&gt;

&lt;p&gt;When I realized I never clicked on any of the external links in the posts I found, I stopped adding external links to my posts.&lt;/p&gt;

&lt;p&gt;And I noticed how my favorite creators structured their posts. So I ditched emojis and started to add blank lines, use shorter sentences, and make my posts mobile friendly.&lt;/p&gt;

&lt;h2 id=&quot;writing-is-learned-mainly-by-imitation&quot;&gt;“Writing is learned mainly by imitation”&lt;/h2&gt;

&lt;p&gt;That’s from &lt;a href=&quot;/2025/01/20/WritingToLearn/&quot;&gt;Writing to Learn by William Zinsser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To improve your writing, imitate your favorite writers to develop your own style.&lt;/p&gt;

&lt;p&gt;Notice the opening lines of your favorite books, collect headlines, and hand-write your favorite pieces. That’s how you get better at writing.&lt;/p&gt;

&lt;p&gt;To learn to write, stop blindly consuming content. Put on your creator glasses and start noticing your behavior as a reader. That’s your cheat code to better writing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>You Don&apos;t Have to Write a New York Times Bestseller to Tell Your Story</title>
   <link href="https://canro91.github.io/2025/04/22/BookForGrandkids/"/>
   <updated>2025-04-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/22/BookForGrandkids</id>
   <content type="html">&lt;p&gt;The definition of book has changed.&lt;/p&gt;

&lt;p&gt;The other day, while reading James Altucher’s archive, I learned a book doesn’t have to be a 10,000-word New York Times bestseller, traditionally published.&lt;/p&gt;

&lt;p&gt;A book could be a 20-page personal story or &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;a summary of 10 scientific papers&lt;/a&gt;. And you don’t have to write it for the masses.&lt;/p&gt;

&lt;p&gt;A book could simply be a story for our grandkids.&lt;/p&gt;

&lt;p&gt;It doesn’t matter if it doesn’t sell thousands of copies or get 5-star reviews or get featured in newspapers. The real audience of our book is 4 or 5 people, our future relatives.&lt;/p&gt;

&lt;p&gt;It would be great to read how grandpa found his first job, met grandma, and made it through life. I wish I had a firsthand glimpse of their lives.&lt;/p&gt;

&lt;p&gt;You don’t need 10,000 words or a traditional publisher. You just need a story to tell. And chances are you already have one.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>One Question to Get Unstuck</title>
   <link href="https://canro91.github.io/2025/04/21/GetUnstuck/"/>
   <updated>2025-04-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/21/GetUnstuck</id>
   <content type="html">&lt;p&gt;A big project might seem daunting. Too many tasks, too many possible outcomes, and the fear of failure.&lt;/p&gt;

&lt;p&gt;If you want to write a book, you might ask: What if nobody buys it? What if nobody reads it? What if it only gets bad reviews?&lt;/p&gt;

&lt;p&gt;When stuck, answer: What’s the worst thing that could happen?&lt;/p&gt;

&lt;p&gt;After identifying that “worst thing,” we realize all this uncertainty and fear was just noise in our heads. And that worst thing wasn’t that bad after all.&lt;/p&gt;

&lt;p&gt;If nobody buys or reads your book, you still learned to outline a book, tell stories, choose a cover, buy an ISBN, and upload it to Amazon. The worst thing wasn’t that bad after all. You were only drowning in a glass of water. And you see how small your fear really was.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How MedAI Will Transform Hospitals and Patient Care by 2035: My Predictions</title>
   <link href="https://canro91.github.io/2025/04/20/MedAI/"/>
   <updated>2025-04-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/20/MedAI</id>
   <content type="html">&lt;p&gt;It’s 2035. AI didn’t kill many jobs as we thought in 2025.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;The world still needs coders&lt;/a&gt;. There are still pilots in the cockpits of airplanes. And there are still doctors and nurses in hospitals.&lt;/p&gt;

&lt;p&gt;But AI has changed many jobs. Just like Photoshop changed graphic design and AutoCad, architecture.&lt;/p&gt;

&lt;h2 id=&quot;one-day-youre-not-feeling-well-and-you-have-to-rush-to-the-er&quot;&gt;One day, you’re not feeling well. And you have to rush to the ER.&lt;/h2&gt;

&lt;p&gt;Just like in 2025, a nurse receives you at the hospital.&lt;/p&gt;

&lt;p&gt;But there’s something different this time. The hospital is run using &lt;strong&gt;MedAI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As you describe your symptoms to a nurse, MedAI transcribes them and creates an initial report. It also studies your changes in intonation and breathing patterns to tell how sick you are or if you’re just looking for an excuse to skip work today.&lt;/p&gt;

&lt;p&gt;MedAI helps the nurse in triage, making sure you receive medical attention as quickly as possible. While MedAI processes all this data, the nurse can focus on preparing you for the next steps.&lt;/p&gt;

&lt;h2 id=&quot;after-waiting-for-a-few-minutes-in-a-room-a-doctor-calls-you-into-the-examination-room&quot;&gt;After waiting for a few minutes in a room, a doctor calls you into the examination room.&lt;/h2&gt;

&lt;p&gt;By the time you get there, MedAI has summarized your records from past visits.&lt;/p&gt;

&lt;p&gt;It has told your doctor when you visited the hospital and your diagnosis on each visit. Also, MedAI helped your doctor determine if your new symptoms are somehow related to past visits.&lt;/p&gt;

&lt;p&gt;Your doctor doesn’t need to examine your vitals.&lt;/p&gt;

&lt;p&gt;Using cameras and sensors, MedAI monitors your vitals: temperature, breathing, and heart rate. Are you vomiting? Shivering uncontrollably? Struggling to stand without help? MedAI quickly assesses these symptoms, allowing your doctor to focus on examining where it hurts.&lt;/p&gt;

&lt;p&gt;After examining you, MedAI scans the hospital database for genetic conditions or recent similar cases among your relatives or other patients. Is it a family problem? A virus outbreak in the city?&lt;/p&gt;

&lt;p&gt;That will give your doctor more clues for your diagnosis.&lt;/p&gt;

&lt;h2 id=&quot;your-doctor-gives-you-an-initial-treatment-to-relieve-your-pain-and-orders-you-some-tests&quot;&gt;Your doctor gives you an initial treatment to relieve your pain and orders you some tests.&lt;/h2&gt;

&lt;p&gt;MedAI has helped your doctor prescribe any medicines based on your allergies or other health issues.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Dr Williams, this patient is allergic to XYZ. And on a past visit, this treatment increased his blood pressure. Here are some alternatives to those medications: …“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By the time your test results are ready. MedAI has summarized the main findings. &lt;em&gt;“He has low XYZ, high ABC. Everything else seems normal for his sex, age, and health condition.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Armed with your vitals, exams, and preliminary diagnosis, MedAI has narrowed down your diagnosis to one or two issues and suggested a treatment for each one.&lt;/p&gt;

&lt;p&gt;Your doctor has to double-check those suggestions and approve your treatment. You have to stay at the hospital for close observation.&lt;/p&gt;

&lt;h2 id=&quot;while-you-are-under-observation-medai-creates-a-daily-report-of-your-evolution&quot;&gt;While you are under observation, MedAI creates a daily report of your evolution.&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“His blood pressure has been stable. No more signs of pain. A change of medication is needed in the next two hours.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Also, MedAI has created a dietary plan for your visit, recommending the right food for you, given your overall health condition. &lt;em&gt;“This is a patient with sugar problems. Here’s a list of recommended meals for his stay…“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your doctor approves it and MedAI sends it to the hospital kitchen.&lt;/p&gt;

&lt;h2 id=&quot;after-a-day-or-two-you-feel-ok-again&quot;&gt;After a day or two, you feel OK again.&lt;/h2&gt;

&lt;p&gt;Your doctor prioritized your well-being and gave you the emotional support only a charismatic doctor can give.&lt;/p&gt;

&lt;p&gt;Meanwhile MedAI assisted him at each step of your treatment, from monitoring your vitals to making sure your treatment was suited to your needs. MedAI was the caring and supportive assistant, but your doctor was still in charge. Just like a pilot is still in charge of a plane through the skies.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Open again in 2035.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Simple Ways to Calm Your Mind in Stressful Moments</title>
   <link href="https://canro91.github.io/2025/04/19/CalmDown/"/>
   <updated>2025-04-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/19/CalmDown</id>
   <content type="html">&lt;p&gt;Yesterday, it took me nearly half an hour to calm down.&lt;/p&gt;

&lt;p&gt;I had just rushed to the ER with a loved one. And I simply couldn’t switch off my brain’s fight response. All the stress chemicals must have been at their highest.&lt;/p&gt;

&lt;p&gt;Once the emergency passed, my body refused to relax. I was still in fight mode. To take control back, I tricked my brain into focusing on something else: &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;writing a 10-idea list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are some other alternatives to try when stressed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Follow the 3-3-3 breathing rhythm:&lt;/strong&gt; Inhale for 3 seconds, hold for another 3, and exhale for 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Write 10 ideas about anything:&lt;/strong&gt; Give your brain another task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Do some physical exercise:&lt;/strong&gt; Go running or lift some weights. Move anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Listen to Dark Side of the Moon:&lt;/strong&gt; I’m not a fan of Pink Floyd. In fact, that’s the only album I’ve known from them. But for some reason, I found it relaxing, especially track number 4, The Great Gig in the Sky. I feel those cries of desperation just like mine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Listen to relaxing sounds:&lt;/strong&gt; A river, ocean, or birds singing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Watch stand-up comedy:&lt;/strong&gt; You can’t be happy and stressed at the same time. Trick your brain into changing its mood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Watch an episode of a TV show:&lt;/strong&gt; TV can help you get out of your busy mind and distract you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Meditate:&lt;/strong&gt; Similar to #1. Repeat a mantra like: “breathe, calm down, and get out of your mind.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Pray:&lt;/strong&gt; Let your feelings out of your chest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Do some cleaning:&lt;/strong&gt; Again, it’s a way to trick your brain into doing something else. It helps you get out of your mind and into something physical.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Friday Links: Vibe Coding, Post-Developer Era, and Blogging</title>
   <link href="https://canro91.github.io/2025/04/18/FridayLinks/"/>
   <updated>2025-04-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/18/FridayLinks</id>
   <content type="html">&lt;p&gt;Hey, there.&lt;/p&gt;

&lt;p&gt;Here are 4 links I thought were worth sharing this week:&lt;/p&gt;

&lt;p&gt;#1. There’s a difference between a program and a product. It’s not the same hacking some lines for a one-time task as crafting an API for concurrent users. &lt;a href=&quot;https://dylanbeattie.net/2025/04/11/the-problem-with-vibe-coding.html&quot;&gt;Vibe coding shines at one of the two&lt;/a&gt; (3min).&lt;/p&gt;

&lt;p&gt;#2. Google claims AI generates 20% of its code. But those claims make us &lt;a href=&quot;https://www.joshwcomeau.com/blog/the-post-developer-era/&quot;&gt;live in a post-developers era&lt;/a&gt;? (12min). AI is like driving a car in cruise mode. It goes where you point it, but you still need your hands on the steering wheel.&lt;/p&gt;

&lt;p&gt;#3. Here are &lt;a href=&quot;https://blog.rpanachi.com/after-25-years-writing-software-here-are-a-few-more-things-ive-learned-so-far-part2&quot;&gt;over 20 coding lessons&lt;/a&gt; (6min) from a coder with 25 years in the field. I had to learn the one about tracking request and responses the hard way.&lt;/p&gt;

&lt;p&gt;#4. I’ll keep my personal blog, even if blogging is dead. A blog is a place without rules to share anything you want. If you have something to say, &lt;a href=&quot;https://shellsharks.com/just-put-it-on-your-blog&quot;&gt;just put it on your blog&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And in case you missed it, I wrote on my blog about &lt;a href=&quot;https://canro91.github.io/2025/04/12/WakeUpTheExperts/&quot;&gt;“waking up experts” when specs aren’t clear&lt;/a&gt; (2min) and &lt;a href=&quot;https://canro91.github.io/2025/04/15/FirstTimeISawAComputer/&quot;&gt;the first time I saw a computer&lt;/a&gt; (2min).&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Here’s this week’s tip to get better at coding:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn some sort of automated testing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Call it unit, integration, TDD, BDD, or anything else.&lt;/p&gt;

&lt;p&gt;Chances are there are formal studies about the time required to maintain and fix bugs in codebases with unit tests compared to codebases without them.&lt;/p&gt;

&lt;p&gt;But, empirically, the healthiest codebases I’ve worked with had unit tests.&lt;/p&gt;

&lt;p&gt;Unit tests are a safety net when you’re making changes. Your coding life will be better with unit tests than without them. And you’ll miss unit tests when you work with a legacy application.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;(Bzzz…Radio voice)&lt;/em&gt; This email was brought to you by…Join my course, &lt;a href=&quot;https://www.udemy.com/course/csharp-nullreferenceexception-demystified/?referralCode=CC2A6F51EF27A75F1364&quot;&gt;C# NullReferenceException Demystified&lt;/a&gt; and in just 1 hour and 5 minutes, you’ll learn the principles, features, and strategies to get rid of that exception.&lt;/p&gt;

&lt;p&gt;See you next time,&lt;/p&gt;

&lt;p&gt;Cesar&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>12 Hard Truths About Coding I Learned the Hard Way After 10 Years</title>
   <link href="https://canro91.github.io/2025/04/17/HarshTruths/"/>
   <updated>2025-04-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/17/HarshTruths</id>
   <content type="html">&lt;p&gt;I got fired from my first job, took down a database server with a badly written query, and was rejected from a FAANG. That all happened over the past 10 years.&lt;/p&gt;

&lt;p&gt;But I’ve learned a lesson or two about coding along the way:&lt;/p&gt;

&lt;h2 id=&quot;1-estimates-are-just-guesses&quot;&gt;1. Estimates are just guesses.&lt;/h2&gt;

&lt;p&gt;The problem is when your guesses don’t overlap with everybody else’s guesses.&lt;/p&gt;

&lt;h2 id=&quot;2-showing-progress-is-better-than-doing-the-work&quot;&gt;2. Showing progress is better than doing the work.&lt;/h2&gt;

&lt;p&gt;Did you guess you can finish a task in 4 days? No, no, no. You’re better off splitting it into smaller ones to show progress.&lt;/p&gt;

&lt;h2 id=&quot;3-proofs-of-concept-are-better-than-long-documents-nobody-will-read&quot;&gt;3. Proofs of concept are better than long documents nobody will read.&lt;/h2&gt;

&lt;p&gt;When was the last time you read more than 2 or 3 pages of documentation?&lt;/p&gt;

&lt;p&gt;You’re better off creating a quick and dirty Pull Request to show an idea or a prototype. “Working code over documentation.”&lt;/p&gt;

&lt;h2 id=&quot;4-the-work-isnt-over-when-you-finish-coding&quot;&gt;4. The work isn’t over when you finish coding.&lt;/h2&gt;

&lt;p&gt;After coding comes deployment, testing, user adoption, customer support, and follow-up.&lt;/p&gt;

&lt;h2 id=&quot;5-the-more-senior-you-become-the-less-its-about-coding-and-the-more-about-meetings&quot;&gt;5. The more senior you become, the less it’s about coding and the more about meetings.&lt;/h2&gt;

&lt;p&gt;Did you join Software Engineering because you like coding? Forget about that.&lt;/p&gt;

&lt;p&gt;You’ll spend more time in meetings:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;1-on-1s,&lt;/li&gt;
  &lt;li&gt;Daily meetings,&lt;/li&gt;
  &lt;li&gt;Retrospectives,&lt;/li&gt;
  &lt;li&gt;Sprint planning,&lt;/li&gt;
  &lt;li&gt;Alignment meetings,&lt;/li&gt;
  &lt;li&gt;Brainstorming sessions,&lt;/li&gt;
  &lt;li&gt;Poker estimation sessions,&lt;/li&gt;
  &lt;li&gt;A few minutes with a guy from another team who needs to touch the code you wrote one year ago and you don’t remember now.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And on and on…&lt;/p&gt;

&lt;p&gt;In a perfect day, you’ll have 1 or 2 hours of coding without distractions.&lt;/p&gt;

&lt;h2 id=&quot;6-you-learn-to-love-tests-when-you-work-with-a-legacy-app&quot;&gt;6. You learn to love tests when you work with a legacy app.&lt;/h2&gt;

&lt;p&gt;Call it unit, integration, end to end, TDD, BDD, or anything DD.&lt;/p&gt;

&lt;p&gt;You’re better off with anything that lets you know when you break something before shipping your code.&lt;/p&gt;

&lt;h2 id=&quot;7-dont-start-a-big-major-refactoring-if-nobody-asks-you-to&quot;&gt;7. Don’t start a big major refactoring if nobody asks you to.&lt;/h2&gt;

&lt;p&gt;This is what I call &lt;a href=&quot;/2023/09/04/AgainstMassiveUnrequestedRefactorings/&quot;&gt;Massive Unrequested Refactorings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Either you refactor as part of your tasks or make it official as part of your sprint planning. There’s no point in between.&lt;/p&gt;

&lt;h2 id=&quot;8-dont-waste-time-on-pointless-discussions-about-tools-or-tech&quot;&gt;8. Don’t waste time on pointless discussions about tools or tech.&lt;/h2&gt;

&lt;p&gt;—”Entity Framework is the best.”
—”No, it’s painfully slow.”
—”Nooo, stored procedures are the best”&lt;/p&gt;

&lt;p&gt;Arrggg!&lt;/p&gt;

&lt;p&gt;Tools are just tools. That’s why we, coders, have the reputation of being opinionated and grumpy. And please, let’s not talk about clean code and best practices. That’s &lt;a href=&quot;/2025/03/19/ChangedMyMind/&quot;&gt;a subject I changed my mind about&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;9-always-automate-code-style-and-best-practices&quot;&gt;9. Always automate code style and best practices.&lt;/h2&gt;

&lt;p&gt;Don’t ask a human to do the work of a machine. &lt;a href=&quot;/2025/03/10/AutomateCodeStyle/&quot;&gt;Code style perfectly matches the type of work for machines&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;10-projects-fail-because-of-communication-problems&quot;&gt;10. Projects fail because of communication problems.&lt;/h2&gt;

&lt;p&gt;The same thing I’ve heard about marriages.&lt;/p&gt;

&lt;p&gt;At a past job, I was engaged in 3- or 6-month projects. We used the shiniest and brightest tools and frameworks. But some projects ended up off the rails.&lt;/p&gt;

&lt;p&gt;The only moving variable? Our communication patterns: Failing to communicate expectations, project goals and scope, action plans, and technical issues on time.&lt;/p&gt;

&lt;h2 id=&quot;11-every-tech-problem-is-a-communication-problem&quot;&gt;11. Every tech problem is a communication problem.&lt;/h2&gt;

&lt;p&gt;It’s a corollary of #10.&lt;/p&gt;

&lt;p&gt;At a past job, I was new to ASP.NET Core and when trying to test my code, I changed a connection string in a settings file and ended up deleting another environment database. Ooops!&lt;/p&gt;

&lt;p&gt;I used the wrong settings file in the wrong environment. I didn’t ask, nobody told me, and there wasn’t any restrictions or guards in the code.&lt;/p&gt;

&lt;p&gt;It was a communication problem.&lt;/p&gt;

&lt;h2 id=&quot;12-solve-the-problem-you-have-today&quot;&gt;12. Solve the problem you have today.&lt;/h2&gt;

&lt;p&gt;Premature optimization or just being lazy, don’t solve and optimize for a problem you don’t have yet. Avoid writing just-in-case code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Three Actions That Changed My Writing from Crickets to Likes</title>
   <link href="https://canro91.github.io/2025/04/16/WritingChanges/"/>
   <updated>2025-04-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/16/WritingChanges</id>
   <content type="html">&lt;p&gt;For almost 5 years, I wrote and only heard crickets.&lt;/p&gt;

&lt;p&gt;Then, everything changed when I bought &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;my first writing course&lt;/a&gt; and started taking writing seriously, not just for fun, but something I was committed to improving.&lt;/p&gt;

&lt;p&gt;I started to see more likes, comments, and shares on my content when I started doing these three things:&lt;/p&gt;

&lt;h2 id=&quot;1-study-writing-subskills&quot;&gt;1. Study writing subskills&lt;/h2&gt;

&lt;p&gt;There’s more to writing than just typing.&lt;/p&gt;

&lt;p&gt;Writing is a set of subskills to master: &lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;headlines&lt;/a&gt;, introductions, outlines, storytelling, copywriting, cliff-hangers, and conclusions.&lt;/p&gt;

&lt;p&gt;I took a notepad and wrote a list of 10 writing skills I wanted to learn. Then I started working on each of those subskills one by one.&lt;/p&gt;

&lt;p&gt;Instead of long and boring introductions, I started using one-sentence openings to attract readers into the rest of my writing.&lt;/p&gt;

&lt;h2 id=&quot;2-hand-copying-to-go-to-the-writing-gym&quot;&gt;2. Hand-copying to go to the writing gym&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Writing is learned mainly by imitation”—&lt;a href=&quot;/2025/01/20/WritingToLearn/&quot;&gt;Writing to Learn by William Zinsser&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I started copying sample pieces by hand as an exercise to &lt;a href=&quot;/2024/12/20/CopywritingStudyPlan/&quot;&gt;learn enough copywriting to be dangerous&lt;/a&gt;. But I also applied it beyond copywriting.&lt;/p&gt;

&lt;p&gt;When we write by hand, we’re forced to slow down and notice and absorb patterns. So next time, we sit down to write, we’ll have those patterns in the back of our heads.&lt;/p&gt;

&lt;p&gt;Grab a post or a book section from your favorite writer and copy it by hand. These days, I’m hand-copying some of &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;’s books.&lt;/p&gt;

&lt;p&gt;Copy, imitate, and then adapt it to your own style.&lt;/p&gt;

&lt;h2 id=&quot;3-tell-more-stories&quot;&gt;3. Tell more stories&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Let Wikipedia spit out facts, you have to spit out stories”—Tim Denning&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There’s a reason why a Wikipedia article or a scientific paper sounds soulless and emotionless. They’re full of facts. They’re written for a selected few. They’re not written to be remembered.&lt;/p&gt;

&lt;p&gt;The easiest way to make our writing memorable is with stories.&lt;/p&gt;

&lt;p&gt;I could tell you, &lt;em&gt;“No job is safe.”&lt;/em&gt; You’ll probably nod in agreement and forget it. But if I tell you, &lt;em&gt;“The day my boss called me to his office and asked me to hand in my company ID, I learned there was no safe job.”&lt;/em&gt; Chances are you’ll remember the second one more.&lt;/p&gt;

&lt;p&gt;We’re driven by stories. That’s what we’ve been doing as humans since we climbed down from trees and sat around the fire in caverns.&lt;/p&gt;

&lt;p&gt;Thanks to sharing my stories from my past jobs, I finally saw traction with my writing. I finally got more than two likes and not just “Thanks for sharing” as comments.&lt;/p&gt;

&lt;p&gt;If you want to &lt;a href=&quot;/2025/02/15/FightingAIContent/&quot;&gt;stand out from AI-generated content&lt;/a&gt;, don’t just share facts. AI can generate facts. Only you can generate stories that connect.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The First Time I Saw a Computer—A Bit of Nostalgia</title>
   <link href="https://canro91.github.io/2025/04/15/FirstTimeISawAComputer/"/>
   <updated>2025-04-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/15/FirstTimeISawAComputer</id>
   <content type="html">&lt;p&gt;An owl blinking in a large forest made us all jump in surprise.&lt;/p&gt;

&lt;p&gt;It was back in 4th grade.&lt;/p&gt;

&lt;p&gt;We all were sitting in a large conference room while a guest teacher disassembled a computer. He took every part, showed it to the class, and told us its name.&lt;/p&gt;

&lt;p&gt;# # #&lt;/p&gt;

&lt;p&gt;That year, the school competition was to build a computer out of recycled materials.&lt;/p&gt;

&lt;p&gt;I built mine with boxes and Styrofoam. OK, when I say “I built,” I mean my mom or my aunt. I don’t remember exactly who. My display was made with an old X-ray image.&lt;/p&gt;

&lt;p&gt;We exhibited our computers on tables in the school hall while a group of teachers walked around taking notes, trying to find the most creative one.&lt;/p&gt;

&lt;p&gt;I didn’t win the competition. My X-ray display wasn’t enough to win.&lt;/p&gt;

&lt;p&gt;But after the competition, I put my computer on my desk at home. I pretended to work with it. Sometimes I still pretend.&lt;/p&gt;

&lt;p&gt;# # #&lt;/p&gt;

&lt;p&gt;After putting all the parts back in place, the guest teacher turned it on.&lt;/p&gt;

&lt;p&gt;We all jumped in surprise. We started pointing at an owl blinking in a large forest. After a few seconds, the forest became an enchanted house with bats flying around. I was frozen sitting next to my friends, staring at that thing. Whatever it was that thing.&lt;/p&gt;

&lt;p&gt;It was an animated wallpaper. LOL. It must have been Windows 95 or 98.&lt;/p&gt;

&lt;p&gt;# # #&lt;/p&gt;

&lt;p&gt;I don’t remember what happened to my first computer ever.&lt;/p&gt;

&lt;p&gt;But, fast forward a few years, we had our first real computer at home. Or my second one.&lt;/p&gt;

&lt;p&gt;It was a Windows 98 computer with a 56K internet connection plugged into a wired phone line. Oh! The famous buzz when we picked up the phone while my dad was connected to the internet. The days of Yahoo and Altavista.&lt;/p&gt;

&lt;p&gt;Only my dad used the computer because “it wasn’t a toy.”&lt;/p&gt;

&lt;p&gt;We had to restart the computer after every minor configuration and every software installation. We used a protection filter on our displays. They were almost radioactive. And after using it, we put pajamas on our computer before going to sleep. Dust was its arch-enemy.&lt;/p&gt;

&lt;p&gt;My favorite wallpaper was an astronaut jumping around in outer space. That was Windows 98.&lt;/p&gt;

&lt;p&gt;And that’s how old I am.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two More Blogging Tips to Try</title>
   <link href="https://canro91.github.io/2025/04/14/TwoBloggingTips/"/>
   <updated>2025-04-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/14/TwoBloggingTips</id>
   <content type="html">&lt;h2 id=&quot;1-title-your-reaction-posts-with-re&quot;&gt;1. Title your reaction posts with “Re:”&lt;/h2&gt;

&lt;p&gt;I’ve written response or reaction posts before, but I named them with my own titles. Yesterday I found this idea to &lt;a href=&quot;https://rubenerd.com/using-re-in-blog-titles/&quot;&gt;use “Re:” in titles&lt;/a&gt; for response posts.&lt;/p&gt;

&lt;h2 id=&quot;2-make-your-urls-easy-to-pronounce&quot;&gt;2. Make your urls easy to pronounce.&lt;/h2&gt;

&lt;p&gt;I already learned from Derek Sivers to &lt;a href=&quot;https://sive.rs/su&quot;&gt;write short and memorable URLs&lt;/a&gt;. But this time, I also learned to &lt;a href=&quot;https://jamesg.blog/2025/04/12/how-audible-is-this-url&quot;&gt;make them easy to pronounce&lt;/a&gt;. For example write URLs using “dash,” which is easier to pronounce than “underscore.”&lt;/p&gt;

&lt;p&gt;More blogging tips? &lt;a href=&quot;/2024/11/13/BlogMore/&quot;&gt;Here are two tips&lt;/a&gt; I’ve been following. They’ve helped me keep my &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;daily blogging streak&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This One Mistake Might Be Scaring Readers Away From Your Writing</title>
   <link href="https://canro91.github.io/2025/04/13/BlamingReaders/"/>
   <updated>2025-04-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/13/BlamingReaders</id>
   <content type="html">&lt;p&gt;Imagine stepping into a restaurant, only for the waiter to say, “You don’t seem to have enough money to eat here.” Would you stay? I know I wouldn’t.&lt;/p&gt;

&lt;p&gt;Never, ever, blame your readers.&lt;/p&gt;

&lt;p&gt;That’s a mistake I’ve learned to avoid in my own writing, especially when writing sales copy for landing pages.&lt;/p&gt;

&lt;h2 id=&quot;instead-of-blaming-your-readers-make-them-feel-heard-and-understood&quot;&gt;Instead of blaming your readers, make them feel heard and understood.&lt;/h2&gt;

&lt;p&gt;The other day, someone tried to sell me access to an online community and shared his landing page. The headline? “You didn’t go to Harvard. You didn’t apply to McKinsey.” He was trying to sell leadership coaching.&lt;/p&gt;

&lt;p&gt;Another day, someone shared the landing page of a fiction writing course he was preparing. The headline? “You don’t know how to tell good stories.”&lt;/p&gt;

&lt;p&gt;I didn’t want to continue reading past the headline of those two pages. I felt like a schoolboy getting on the bus, walking down the aisle, hoping for someone to smile back—only to be ignored or bullied.&lt;/p&gt;

&lt;p&gt;A headline should attract the right audience.&lt;/p&gt;

&lt;h2 id=&quot;but-we-should-attract-the-right-audience-without-making-them-feel-dismissed-or-rejected&quot;&gt;But we should attract the right audience without making them feel dismissed or rejected.&lt;/h2&gt;

&lt;p&gt;Someone who feels dismissed or rejected stops reading, and worse, they won’t buy.&lt;/p&gt;

&lt;p&gt;A couple of alternatives for those landing pages:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“You don’t need to spend 4 years and pay Harvard tuition to be a leader people want to work with.”&lt;/li&gt;
  &lt;li&gt;“It’s hard to tell good stories. I tried plenty of templates and frameworks. None of them worked. Until I learned this method.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Boom! Just like that, no more pointing fingers at the reader. Would you keep reading if those two were the headlines? After all, nobody likes being blamed.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>When Specs Are Unclear, Wake Up the Experts</title>
   <link href="https://canro91.github.io/2025/04/12/WakeUpTheExperts/"/>
   <updated>2025-04-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/12/WakeUpTheExperts</id>
   <content type="html">&lt;p&gt;“Do anything to wake up the experts.”&lt;/p&gt;

&lt;p&gt;That was a phrase we repeated at a past job. We were building a gateway-like software to connect small companies to the Government’s tax office.&lt;/p&gt;

&lt;p&gt;It took us a couple of years to build a minimum viable product. We were behind the Government’s changing requirements and our own clients’ expectations.&lt;/p&gt;

&lt;p&gt;We were late almost all the time. Often we knew we needed to finish something, but we weren’t sure what or how. Apart from emails or conversations with clients, we didn’t have clear requirements or specs.&lt;/p&gt;

&lt;p&gt;In those moments, we woke up the experts.&lt;/p&gt;

&lt;p&gt;To show some progress, we built on a quick prototype or a half-baked idea. Then, after looking at that half-baked idea, a stakeholder, project manager, or product owner, would speak up, saying that what we had wasn’t what was needed. Then, we started to work on a real solution.&lt;/p&gt;

&lt;p&gt;When looking at a bad answer or a half-baked idea, it turned out everyone was an expert.&lt;/p&gt;

&lt;p&gt;The fastest way to get a good answer is by giving a bad answer, by waking up the experts.&lt;/p&gt;

&lt;p&gt;For more workplace lessons, check &lt;a href=&quot;/2024/02/05/LessonsOnAFinishedProject/&quot;&gt;the leadersship lessons I learned from this project&lt;/a&gt; and &lt;a href=&quot;/2024/12/11/SelfManagedTeam/&quot;&gt;the lesson from the most expensive hambugers I’ve tried&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Where to Start Your Coding Career: Corporate, Tech, or Agency?</title>
   <link href="https://canro91.github.io/2025/04/11/CorporateTechOrAgency/"/>
   <updated>2025-04-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/11/CorporateTechOrAgency</id>
   <content type="html">&lt;p&gt;If you’re looking to start your coding career, start by understanding each company type has its own vibe.&lt;/p&gt;

&lt;p&gt;These days, &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;stability and job security are hard to find&lt;/a&gt;. Recession, high interest rates, layoffs, AI, you name it.&lt;/p&gt;

&lt;p&gt;In over 10 years, I’ve worked in non-tech corporate companies, tech companies, and software agencies. This is what I’ve found.&lt;/p&gt;

&lt;h2 id=&quot;non-tech-corporate-companies&quot;&gt;Non-tech corporate companies&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;I started at a “boring” job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I was at the IT department of a large company in my city. That was my first contact with office politics and the corporate world. Spoiler alert: &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;I was fired&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This type of company in one word? Slow.&lt;/p&gt;

&lt;p&gt;If you land a job here, expect more office politics and bureaucracy. The larger the company, the more you’ll find. They tend to offer more benefits and might feel stable.&lt;/p&gt;

&lt;p&gt;But don’t expect to work on the shiniest and brightest tech stack or tools. Be ready to work on a legacy codebase with outdated or little documentation, fixing bugs and hacking to add new features without breaking anything.&lt;/p&gt;

&lt;h2 id=&quot;tech-companies&quot;&gt;Tech companies&lt;/h2&gt;

&lt;p&gt;Tech companies tend to move faster than corporate jobs.&lt;/p&gt;

&lt;p&gt;In a tech company, you’re helping the company make money. You aren’t an expense anymore. That often means higher salaries.&lt;/p&gt;

&lt;p&gt;This is the place where you’d find TDD, &lt;a href=&quot;/2025/03/16/DDDIsNotAboutEntities/&quot;&gt;DDD&lt;/a&gt;, any other DD methodology, &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;code reviews&lt;/a&gt;, Kubernetes, the Cloud, and the shiniest and brightest.&lt;/p&gt;

&lt;p&gt;But, they also come with &lt;a href=&quot;/2025/03/22/IsBurnoutInevitable/&quot;&gt;higher risk of burning out&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;software-agencies&quot;&gt;Software agencies&lt;/h2&gt;

&lt;p&gt;Here, you are an employee assigned to a client company or project.&lt;/p&gt;

&lt;p&gt;There’s good money with agencies if you live in a place with low cost of living and earn your salary in stronger currencies. Money will pass from the agency to your bank, while they take a good chunk of it. That’s the business.&lt;/p&gt;

&lt;p&gt;Also, understand that companies prefer agencies as a risk-free alternative to hiring. When they don’t need more hands on a project, they’re just an email away from &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;letting people go&lt;/a&gt;. Again, that’s the business.&lt;/p&gt;

&lt;p&gt;Expect client rotation and possibly months without pay while the agency finds you another client.&lt;/p&gt;

&lt;p&gt;With agencies, there are fewer chances of growth since you’re sold as a pair of hands.&lt;/p&gt;

&lt;p&gt;Of course, YMMV.&lt;/p&gt;

&lt;p&gt;Rather than choosing a company for its benefits, start experimenting with your career, then make a 5-year career plan (or intention), and pick the jobs and places that take you closer to that plan. That’s &lt;a href=&quot;/2025/03/04/CareerLessonIWishIKnew/&quot;&gt;a lesson I wish I had known 10 years ago&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Want To Write As A Coder? Start With TIL Posts</title>
   <link href="https://canro91.github.io/2025/04/10/TilPosts/"/>
   <updated>2025-04-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/10/TilPosts</id>
   <content type="html">&lt;p&gt;If you want to start a coding blog, don’t start with a deep dive of the Linux Kernel or other cryptic topics, unless you’re an expert on them.&lt;/p&gt;

&lt;h2 id=&quot;instead-write-short-today-i-learned-til-posts&quot;&gt;Instead write short “Today I Learned” (TIL) posts.&lt;/h2&gt;

&lt;p&gt;TIL posts are shorter posts where you share something you’ve found or figured out.&lt;/p&gt;

&lt;p&gt;When writing TIL posts, you don’t have to worry about long introductions or conclusions. Just write a good headline, a code block, a quick explanation, and your sources. And &lt;a href=&quot;/2024/12/24/WritingVoice/&quot;&gt;write using your own words&lt;/a&gt;, like in a conversation with a coworker. Here are &lt;a href=&quot;/tags/todayilearned/&quot;&gt;my TIL posts&lt;/a&gt; as example.&lt;/p&gt;

&lt;p&gt;That’s enough to &lt;a href=&quot;/2025/03/11/BloggingTips/&quot;&gt;make a post worth reading&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;til-posts-invite-people-into-your-learning-journey&quot;&gt;TIL posts invite people into your learning journey.&lt;/h2&gt;

&lt;p&gt;Don’t try to lecture the coding world about what they should do. Start documenting your learning instead.&lt;/p&gt;

&lt;p&gt;Instead of writing &lt;em&gt;“5 VS Code extensions every coder should install,”&lt;/em&gt; try &lt;em&gt;“TIL: 5 VS Code extensions I couldn’t avoid installing.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Or instead of &lt;em&gt;“5 Git commands every coder should know,”&lt;/em&gt; covering the same basic Git commands, write &lt;em&gt;“TIL: 5 Git basic commands to use everyday”&lt;/em&gt; or &lt;em&gt;“TIL: How Git Status works.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Did you spend &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;20 minutes or more figuring out something&lt;/a&gt;? write a TIL post. That’s the easiest way to start a coding blog. And &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;don’t think of writing your own blogging engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Apart from coding, writing has been one of the best skills I’ve developed as a coder. It’s taught me to research, organize, and present ideas clearly.&lt;/p&gt;

&lt;p&gt;That’s why I made it one of the strategies in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; It’s the guide I wish I had when I was starting out, trying to go from junior to senior.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=start-with-til-posts&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Just Doing Great Work Isn&apos;t Enough—People Won&apos;t Come</title>
   <link href="https://canro91.github.io/2025/04/09/GreatWorkIsNotEnough/"/>
   <updated>2025-04-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/09/GreatWorkIsNotEnough</id>
   <content type="html">&lt;p&gt;As full-time employees, we only stick to “our part.”&lt;/p&gt;

&lt;p&gt;If we’re coders, we only care about coding. If we’re designers, we only care about Photoshop. Somebody else handles finding clients, marketing, sales, and follow-ups. If we, as full-time employees, care about what’s not “our part,” &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;we stand out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But with a side gig, a SaaS product, a novel, or a hobby to monetize, we have to also do everything else that isn’t “our part.”&lt;/p&gt;

&lt;p&gt;When we do “our part,” the job isn’t done. We have to wear all those hats. We have to be our own marketing, sales, and customer satisfaction departments. We can’t expect others to do that for us.&lt;/p&gt;

&lt;p&gt;Doing great work alone isn’t enough. People won’t just come. Do great work, then make sure the world knows about it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Success Isn&apos;t One-Size-Fits-All. And That&apos;s Okay</title>
   <link href="https://canro91.github.io/2025/04/08/SuccessMetrics/"/>
   <updated>2025-04-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/08/SuccessMetrics</id>
   <content type="html">&lt;p&gt;“And so…what happened?” she said.&lt;/p&gt;

&lt;p&gt;I was with a friend at a supermarket while the cashier passed the products along the band through the machine. There was a copy of “Psychology of Money” on the counter. And I was telling my friend my favorite story from the book.&lt;/p&gt;

&lt;p&gt;“There was a guy in a town. He was the school janitor. You might think janitors don’t make a lot of money.” I said while the cashier was listening. “But after he died, they found out he was a millionaire who donated part of his fortune to the local library.”&lt;/p&gt;

&lt;p&gt;“So…what happened? He worked for nothing?” the cashier asked me.&lt;/p&gt;

&lt;p&gt;“Well, maybe that was his success. Cleaning rooms for kids to study. That was what he enjoyed,” I told her.&lt;/p&gt;

&lt;p&gt;“Nah, nah, nah,” she said while she leaned back, stretching her arms behind her head, “I want to be doing nothing.”&lt;/p&gt;

&lt;p&gt;# # #&lt;/p&gt;

&lt;p&gt;I’ve changed my mind about success lots of times.&lt;/p&gt;

&lt;p&gt;For a while, I thought success meant climbing the corporate ladder to the title of VP in a tech startup Silicon Valley. Then, it was mastering coding, chasing the “expert” label. Then, it was sitting on a beach holding a piña colada while trading and enjoying financial freedom. Then, it was something else.&lt;/p&gt;

&lt;p&gt;But those weren’t my own success metrics.&lt;/p&gt;

&lt;p&gt;They were success metrics I had absorbed without question. From YouTube, from my peers, from the rich. It took me &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;a burnout season&lt;/a&gt; to realize I had been trying to make this &lt;a href=&quot;/2024/10/21/QuickCareerLessons/&quot;&gt;corporate dream work for over 10 years&lt;/a&gt;. Better late than never.&lt;/p&gt;

&lt;p&gt;For the janitor, maybe it was a boring job. For the cashier, doing nothing. But my success metrics these days? Working on things I love and following my passions without worrying about money.&lt;/p&gt;

&lt;p&gt;Your success metrics don’t have to be like mine or like anyone else’s. And that’s fine. But whatever they are, make sure they’re truly yours.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Want to Write for Years? Start by Writing Like No One&apos;s Watching</title>
   <link href="https://canro91.github.io/2025/04/07/NobodyIsReading/"/>
   <updated>2025-04-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/07/NobodyIsReading</id>
   <content type="html">&lt;p&gt;“Hey, how’s your LinkedIn posting doing?” he said.&lt;/p&gt;

&lt;p&gt;I was at a gathering with some friends and ex-coworkers. “I read your posts. I don’t like or comment on them, but I read them.”&lt;/p&gt;

&lt;p&gt;That was surprising. I wasn’t expecting a greeting like that one. That was a small win and encouraged me to &lt;a href=&quot;/2024/12/14/Consistency/&quot;&gt;stay consistent and keep showing up&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last year, &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;I revived my LinkedIn&lt;/a&gt; account and started to use it to share my ideas and build my writing skills in public, instead of simply using it to land new jobs.&lt;/p&gt;

&lt;p&gt;When we start writing online, we expect to have thousands of followers, tons of comments, and to become thought leaders in just a couple of days.&lt;/p&gt;

&lt;p&gt;For some reason, we don’t expect the same when practicing a sport. But we think it’s true for social media and writing online.&lt;/p&gt;

&lt;p&gt;The first months of writing online are lonely. Nothing happens. Nobody likes or comments on any of our posts. Post after post goes nowhere. Just cricket sounds. We’re shouting into the void.&lt;/p&gt;

&lt;p&gt;But those first moments are the ones that make us build our persistence muscles and push us to improve our craft. “I didn’t make it today. There’s another chance tomorrow.”&lt;/p&gt;

&lt;p&gt;Write as if nobody is reading, and keep writing because you don’t know who’s reading.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>10 Features of a Non-Addictive Social Platform</title>
   <link href="https://canro91.github.io/2025/04/06/NoSocial/"/>
   <updated>2025-04-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/06/NoSocial</id>
   <content type="html">&lt;p&gt;Every beep, buzz, and notification is designed to keep us hooked.&lt;/p&gt;

&lt;p&gt;The other day while preparing my &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;Friday Links email&lt;/a&gt;, I found &lt;a href=&quot;https://www.seven39.com/&quot;&gt;Seven39&lt;/a&gt;. A social media platform that only opens from 7:39PM to 10:39PM EST.&lt;/p&gt;

&lt;p&gt;It made me think of what features I’d like to see on a non-addictive social media app:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. It only opens on a fixed schedule.&lt;/strong&gt; Inspired by Seven39 itself. Or, instead of a fixed schedule for everyone, users set their own open hours, capped at two hours per day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. It only allows you to have 150 connections, at most.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. After open hours, it goes into “dumb” mode&lt;/strong&gt; or simply kicks you out, and you can’t log in again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. It only allows a maximum number of interactions&lt;/strong&gt;, such as likes, comments, or reposts, per day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Users are limited to one post per day.&lt;/strong&gt; You’re better off thinking carefully about what you want to share. You only have one shot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. It’s invitation-only.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. It doesn’t email you&lt;/strong&gt;, except for authentication and security reasons. No email notifications at all. No “someone viewed your profile.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. No endless feed for you to scroll.&lt;/strong&gt; You can only interact directly with your connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. It has no notifications while you’re online.&lt;/strong&gt; You should only receive a daily digest of your connections’ activity on your post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. It has no metrics.&lt;/strong&gt; Just likes.&lt;/p&gt;

&lt;p&gt;A platform like this needs a name. What about these?&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;BeepZero: No beeps. No notifications. Just connection.&lt;/li&gt;
  &lt;li&gt;TinCan: Your friends, you, and a tin can phone.&lt;/li&gt;
  &lt;li&gt;NoSocial: The nosocial media platform.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Which one is your favorite? Mine is TinCan.&lt;/p&gt;

&lt;p&gt;Would you sign up for a platform like this? Of course, after you receive an invitation.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>8 Lessons from a Conversation Between @CulturalTutor and David Perell</title>
   <link href="https://canro91.github.io/2025/04/05/CulturalTutor/"/>
   <updated>2025-04-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/05/CulturalTutor</id>
   <content type="html">&lt;p&gt;From cleaning at McDonald’s to 1.7 million followers on Twitter/X.&lt;/p&gt;

&lt;p&gt;Sheehan Quirke (@CulturalTutor) was trying to find ways to make money writing. His best plan was offering tutoring in literature, architecture, and culture. He started his Twitter/X account to drive traffic to his tutoring gig.&lt;/p&gt;

&lt;p&gt;Plot twist? People on Twitter didn’t want his tutoring sessions, but his threads.&lt;/p&gt;

&lt;p&gt;That’s how Sheehan started. He shared his story here:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/1pBxikHRCXI?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;And here are my lessons from that interview:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Don’t read anything written in the last 50 years.&lt;/strong&gt; If you read what everybody else reads, you’ll say same the same things as everybody else. And you’ll think like everybody else. Find good sources of inspiration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Focus on writing good stuff.&lt;/strong&gt; Forget about algorithm hacks and engagement pods. Just write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Write good hooks.&lt;/strong&gt; Our content is competing with memes and 6-pack abs. Our content will pass unnoticed unless we write opening lines that make people stop scrolling. &lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;Steal your hooks from influencers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. The best way to stay consistent is to always believe you can improve.&lt;/strong&gt; Your writing today is practice for better writing tomorrow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Find peers who encourage you.&lt;/strong&gt; This is what James Altucher calls &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;finding your EQUALS&lt;/a&gt;. People on the same journey who can challenge and encourage you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Treat writing as your full-time job.&lt;/strong&gt; A young boy who wants to make it to the Major Leagues knows he has to train and practice every day. Writing shouldn’t be anything different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Focus on one thing.&lt;/strong&gt; It’s easy to get distracted by too many platforms, pretending to be everywhere. Twitter/X, Medium, Substack, LinkedIn, etc. Focus on one thing in one place. When Sheehan was starting on Twitter/X, he wanted to start a newsletter. That would have distracted from what was working: Twitter threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Find ideas on your daily conversations.&lt;/strong&gt; And refine them by talking about them. This sounds like &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;the 3-strike rule to write posts&lt;/a&gt; I found the other day.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Demotivate Your Development Team?</title>
   <link href="https://canro91.github.io/2025/04/04/DemotivateYourTeam/"/>
   <updated>2025-04-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/04/DemotivateYourTeam</id>
   <content type="html">&lt;p&gt;Guaranteed results in 10 simple steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Ignore ideas and suggestions.&lt;/strong&gt; If anyone comes with an idea or suggestion, ignore it. And if anyone raises a concern, say it’s fine and it used to be worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Take credit for someone else’s idea.&lt;/strong&gt; If anyone comes with an idea and you don’t want to ignore it, say “oh, that was what I told you we should do.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Don’t share any vision or project goals.&lt;/strong&gt; Just keep your team closing JIRA tickets. Tickets and more tickets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. When someone asks for a salary review, say “come back in a few months.”&lt;/strong&gt; And then, tell them to come back again. And on and on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5.&lt;/strong&gt; Once a task or project is finished, &lt;strong&gt;publicly praise someone else’s work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Give your team boring and repetitive work.&lt;/strong&gt; The more boring and repetitive, the better. Make them dig holes. Then make them fill those holes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Make them work on projects no one will use.&lt;/strong&gt; Did your team work on a project for six months? Archive it, deprioritize it, or make sure nobody uses it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8.&lt;/strong&gt; Your team worked hard, but why share what the users think? That’s not their business, right? &lt;strong&gt;Don’t share any feedback.&lt;/strong&gt; Just keep them finishing tickets. See #3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Use the word “resource” to refer to them and treat them accordingly.&lt;/strong&gt; Your team members are machines you can replace anytime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Lay off people and tell the ones who stay “nothing is happening.”&lt;/strong&gt; And if they ask about it, tell them they should be grateful for still having a job.&lt;/p&gt;

&lt;p&gt;Follow these steps and you’ll have team members who will leave you at their first chance.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why You Should Change Jobs—According to Jesus</title>
   <link href="https://canro91.github.io/2025/04/03/JesusEffect/"/>
   <updated>2025-04-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/03/JesusEffect</id>
   <content type="html">&lt;p&gt;I’ve finally understood why it’s better to change jobs than to ask for a raise.&lt;/p&gt;

&lt;p&gt;These days, I was reading &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;’s archive of posts and I found out about the Jesus’ effect. I had already heard about the Doppler effect, the Google effect, and the Butterfly effect. But, not about Jesus’ effect.&lt;/p&gt;

&lt;h2 id=&quot;the-jesus-effect-or-just-being-the-carpenters-son&quot;&gt;The Jesus’ effect or just being the “carpenter’s son”&lt;/h2&gt;

&lt;p&gt;According to the Gospels, Jesus was traveling and preaching all over his home country.&lt;/p&gt;

&lt;p&gt;In our modern day, he would be on podcast tours, giving TED talks, and running masterminds. He was creating a brand and growing a following. Literally.&lt;/p&gt;

&lt;p&gt;His movement was getting larger and larger, with followers and haters.&lt;/p&gt;

&lt;p&gt;Until he arrived in his hometown.&lt;/p&gt;

&lt;p&gt;He couldn’t continue doing any of his work. There, he was only the “carpenter’s son.” His hometown was the place with the most haters.&lt;/p&gt;

&lt;p&gt;At that point, I had already heard about that piece of advice: “Don’t ask for a raise, change jobs.” I’ve made the mistake of not following it. Shame on me!&lt;/p&gt;

&lt;h2 id=&quot;but-then-i-made-the-connection-between-jesus-and-changing-jobs&quot;&gt;But then I made the connection between Jesus and changing jobs&lt;/h2&gt;

&lt;p&gt;Often, the best place to grow is not where you’re at now, but somewhere else where people will value you more.&lt;/p&gt;

&lt;p&gt;Where you are now, you’re probably the database guy or the API guy or the UI guy. That’s your perceived value. You’re just the “carpenter’s son.”&lt;/p&gt;

&lt;p&gt;That’s why most conversations about salary raises die with a “come in 3 or 6 months” or “come after your next performance review.” You’ve reached a glass ceiling in terms of growth and how management and check-signers perceive you at work.&lt;/p&gt;

&lt;p&gt;There are no more growth opportunities. No more salary increases. Just the same old grind until you get bored or laid off.&lt;/p&gt;

&lt;p&gt;Often, just like Jesus, we find ourselves defined by labels, the “carpenter’s son” or whatever label you have at work, and to grow, we have to move to another place. Be like Jesus and find opportunities somewhere else where you’re valued more.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Never Knew Kidneys Mattered—Until Dialysis Hit Close to Home</title>
   <link href="https://canro91.github.io/2025/04/02/KidneysDoMatter/"/>
   <updated>2025-04-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/02/KidneysDoMatter</id>
   <content type="html">&lt;p&gt;All marketing goes to hearts and lungs.&lt;/p&gt;

&lt;p&gt;Buy sunflower oil because it’s good for your heart.&lt;/p&gt;

&lt;p&gt;Don’t smoke. And if you buy a pack of cigarettes and it comes with a picture of damaged lungs.&lt;/p&gt;

&lt;p&gt;But what about kidneys? I haven’t seen any ads for them.&lt;/p&gt;

&lt;p&gt;# # #&lt;/p&gt;

&lt;p&gt;“She needs blood transfusions. Sign the consent here,” I was told.&lt;/p&gt;

&lt;p&gt;My loved one was in the ICU. After lots of blood tests, a full-body scan, and urine exams, the diagnosis: chronic kidney disease.&lt;/p&gt;

&lt;p&gt;Two days earlier, we took her to the ER.&lt;/p&gt;

&lt;p&gt;Her abdomen was swollen. She couldn’t hold a simple conversation. She threw up constantly. Sometimes, she couldn’t even recognize us. Her body was poisoning itself.&lt;/p&gt;

&lt;p&gt;Her kidneys had collapsed.&lt;/p&gt;

&lt;p&gt;It was 2020, in the middle of the bat-soup crisis. We were scared of what she had and scared that she might get that deadly thing from the hospital air. Either one could kill her. Two invisible enemies.&lt;/p&gt;

&lt;p&gt;She got the highest priority in triage at the ER. And she didn’t spend long time there before getting into the ICU. A couple of &lt;a href=&quot;/2025/03/21/SuperBlood/&quot;&gt;unknown heroes stood up for her with their blood&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An internist, a urologist, and a nephrologist saw her. She wasn’t fine at all.&lt;/p&gt;

&lt;p&gt;# # #&lt;/p&gt;

&lt;p&gt;Kidneys have two main functions:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Remove excess liquid from your blood.&lt;/li&gt;
  &lt;li&gt;Filter waste and toxins from your blood.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;High blood pressure, diabetes, and kidney infections are signs that you should look for to start taking care of your kidneys more closely.&lt;/p&gt;

&lt;p&gt;Drink enough water, exercise, stop smoking, and avoid salt. Take care of your kidneys.&lt;/p&gt;

&lt;p&gt;Don’t trust me. Trust the &lt;a href=&quot;https://www.niddk.nih.gov/health-information/kidney-disease/chronic-kidney-disease-ckd&quot;&gt;US National Institute of Health&lt;/a&gt;. Or talk to your doctor.&lt;/p&gt;

&lt;p&gt;# # #&lt;/p&gt;

&lt;p&gt;“My life depends on a machine now,” she told me.&lt;/p&gt;

&lt;p&gt;My loved one was shocked after her first session of dialysis.&lt;/p&gt;

&lt;p&gt;When your kidneys are damaged, a machine replaces them. You’re “connected” to a machine that works like your kidneys. It takes out some blood, filters it out, and puts it back in. That happens for 3 or 4 hours, three times a week. That process is called “dialysis.”&lt;/p&gt;

&lt;p&gt;It’s like going to a gym or exercising three times a week to stay healthy for a regular person. The only difference for her? She can’t skip a day or two. There’s more at risk for her than a slim body.&lt;/p&gt;

&lt;p&gt;She first had a small tube in her neck, then in her chest. That’s how she got connected to that machine. And later, a vascular access on her arm (a vein and an artery are connected to get more blood out). This time, she got connected with two large needles. Ouuuch! That’s painful.&lt;/p&gt;

&lt;p&gt;Sitting for 4 hours is uncomfortable.&lt;/p&gt;

&lt;p&gt;But that’s not the worst part after a session of dialysis. High or low blood pressure, headaches, dizziness, and tiredness. That’s the worst part.&lt;/p&gt;

&lt;p&gt;After every session, she eats and then goes right to bed. No more mental or physical energy for anything else. The day is over.&lt;/p&gt;

&lt;p&gt;Does her life depend on a machine? Technically, yes. But we prefer to see it as the machine giving her life. It’s not her enemy, it’s her fighting partner.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>7 Reasons Why I&apos;ll Keep Writing—Even If AI Writes Faster and Better</title>
   <link href="https://canro91.github.io/2025/04/01/WhyIWillKeepWriting/"/>
   <updated>2025-04-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/04/01/WhyIWillKeepWriting</id>
   <content type="html">&lt;p&gt;We can’t compete with AI on speed.&lt;/p&gt;

&lt;p&gt;AI is getting faster, better, stronger, and cheaper. In seconds, it can produce a post about almost any topic in any writing style. An essay about climate change in the style of Hemingway? AI spits it out. Beep, beep, boop!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/02/15/FightingAIContent/&quot;&gt;The battle against AI-generated content is lost&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;even-if-one-day-ai-writes-better-and-faster-ill-still-write-because&quot;&gt;Even if one day AI writes better and faster, I’ll still write because:&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Writing is learning&lt;/strong&gt;: If you want to check if you understand a subject, try writing about it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Writing is teaching&lt;/strong&gt;: You don’t need a lecture hall to teach or share anything. Simply write about it somewhere on the internet.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Writing brings clear thinking&lt;/strong&gt;: This reason is strong enough not to outsource our writing to AI.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Writing is building a time capsule&lt;/strong&gt;: Writing is leaving breadcrumbs of what we were learning and doing. It’s leaving success clues. It’s &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;a time capsule&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Writing is free, public therapy&lt;/strong&gt;: In 2024, &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;I applied to FAANG and failed&lt;/a&gt;. Instead of ranting that hiring is broken, I decided to write about it. It was liberating.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Writing is an exercise for my creative mind&lt;/strong&gt;: After &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;ditching my to-do lists&lt;/a&gt;, writing is part of my daily routine to do something for my body, mind, and spirit each day.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;and-more-importantly-7-writing-helped-me-recover-from-burnout-last-year&quot;&gt;And more importantly, 7. writing helped me recover from burnout last year.&lt;/h2&gt;

&lt;p&gt;Last year, I took officially &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;my first writing class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was the first step toward taking my writing seriously after about &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;five years of blogging&lt;/a&gt;. Writing daily was part of welcoming a sense of joy into my life after all the dissatisfaction and disengagement of burning out.&lt;/p&gt;

&lt;p&gt;In the end, writing rescued me.&lt;/p&gt;

&lt;p&gt;Even if AI splits the world into &lt;a href=&quot;/2024/11/24/WriteNots/&quot;&gt;writers and write-nots&lt;/a&gt;, even if it spits words faster than I ever could, I’ll continue writing, for my health, for my joy, for myself. I’d like to be a writer.&lt;/p&gt;

&lt;p&gt;PS: This is my answer to Ron Markley’s Medium post titled: &lt;a href=&quot;https://ronwayjourney.medium.com/what-if-one-day-ai-writes-better-than-you-will-you-still-write-1ac74803ebb3&quot;&gt;What if one day AI writes better than you? Will you still write?&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Use AI Like We Use Calculators in Math</title>
   <link href="https://canro91.github.io/2025/03/31/AIAndCalculators/"/>
   <updated>2025-03-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/31/AIAndCalculators</id>
   <content type="html">&lt;p&gt;For 8 years, calculators are banned in math class.&lt;/p&gt;

&lt;p&gt;We can only use calculators after we learn to do arithmetic by hand, like multiplying and dividing 2- and 3-figure numbers manually. We have to learn the procedure first.&lt;/p&gt;

&lt;p&gt;We should use AI the same way, only after we know how to do the procedure by hand.&lt;/p&gt;

&lt;p&gt;If you aren’t comfortable with coding yet, &lt;a href=&quot;/2025/03/24/NewCodersAndAI/&quot;&gt;don’t ask AI to generate code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you aren’t comfortable with writing, don’t ask AI to generate any text. Instead, &lt;a href=&quot;/2025/03/01/ReplacingGrammarly/&quot;&gt;use it to proofread&lt;/a&gt; and critique your writing.&lt;/p&gt;

&lt;p&gt;AI should be like calculators in math: a tool to make us faster if we know what we’re doing. They can’t do the thinking part for us. That’s still on us.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>An Easy Mnemonic to Format Dates As Strings in C#</title>
   <link href="https://canro91.github.io/2025/03/30/FormattingDates/"/>
   <updated>2025-03-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/30/FormattingDates</id>
   <content type="html">&lt;p&gt;Is it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MM&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mm&lt;/code&gt; for months when formatting dates? Is it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yyyy-MM-dd&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yyyy-mm-dd&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;I always forgot which one to use…until I figured out an easy mnemonic.&lt;/p&gt;

&lt;h2 id=&quot;mm-vs-mm&quot;&gt;MM vs mm&lt;/h2&gt;

&lt;p&gt;Ask yourself: Which one represents a larger time frame? Months or minutes?&lt;/p&gt;

&lt;p&gt;Since one month is larger than one minute, it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M&lt;/code&gt; for months. An uppercase m is larger than a lowercase m. Write, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anyDate.ToString(&quot;yyyy-MM-dd&quot;)&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;mm-vs-fff&quot;&gt;mm vs fff&lt;/h2&gt;

&lt;p&gt;Then, what is it for milliseconds? Isn’t it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mm&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Now, ask yourself: Which one is faster? One minute or one millisecond?&lt;/p&gt;

&lt;p&gt;Since one millisecond is faster, write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt;. Write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anyDate.ToString(&quot;hh:mm:ss.fff&quot;)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Easy, peasy now!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My Worst Interviews and the Lesson They Taught Me</title>
   <link href="https://canro91.github.io/2025/03/29/WorstInterviews/"/>
   <updated>2025-03-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/29/WorstInterviews</id>
   <content type="html">&lt;p&gt;If you’ve been around long enough in tech, you know hiring is broken.&lt;/p&gt;

&lt;p&gt;We all have horror interview stories. It’s a shared experience all the way from small companies trying to imitate &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;FAANG processes&lt;/a&gt; and large companies with dozens of “phases” to go through.&lt;/p&gt;

&lt;p&gt;I’m not the exception, so here are two of my horror stories and a lesson.&lt;/p&gt;

&lt;h2 id=&quot;they-wanted-to-test-my-iq&quot;&gt;They wanted to test my IQ&lt;/h2&gt;

&lt;p&gt;Before the bubble of 2020, a recruiter from a staffing company reached out on LinkedIn.&lt;/p&gt;

&lt;p&gt;The meeting had an awkward start. The recruiter refused to speak in our native language, but in English. I might understand that, since they worked with American clients.&lt;/p&gt;

&lt;p&gt;But the horrific part came when she told me they were going to test my IQ to see if I was “smart enough” to work with them. At that moment the interview ended for me. Run, Forrest, run!&lt;/p&gt;

&lt;h2 id=&quot;they-wanted-a-screen-recording&quot;&gt;They wanted a screen-recording&lt;/h2&gt;

&lt;p&gt;I’ve been asked to &lt;a href=&quot;/2021/11/22/CodingChallengeTips/&quot;&gt;solve take-home interview challenges&lt;/a&gt; more than once.&lt;/p&gt;

&lt;p&gt;But this time, with another staffing company, I had an unusual interview challenge: not only did I have to solve a medium-size coding exercise, but also record my screen while explaining out loud my thought process, in no more than 2 hours, and send them a link.&lt;/p&gt;

&lt;p&gt;I guess a poor soul had to watch my recording at 2x, if anyone ever watched it. My thought was to create a YouTube channel and solve it live. At least, it would count towards the watch time to monetize a YouTube account. Of course, I didn’t continue.&lt;/p&gt;

&lt;p&gt;Probably I had more horror stories I can’t remember.&lt;/p&gt;

&lt;h2 id=&quot;the-lesson&quot;&gt;The lesson&lt;/h2&gt;

&lt;p&gt;Hiring is a two-way street: you’re evaluating them the same way they’re evaluating you.&lt;/p&gt;

&lt;p&gt;The first meeting shows how they’re going to treat you as an employee. And if &lt;a href=&quot;/2024/12/04/RedFlags/&quot;&gt;you notice red flags&lt;/a&gt; during the process, like unclear expectations and unrealistic demands, imagine what it would be like working there.&lt;/p&gt;

&lt;p&gt;Always ask about the next steps, salary, and compensation in the first meeting.&lt;/p&gt;

&lt;p&gt;When I was job hunting, my rule was to step away if they didn’t share salary details in the first meeting. Why waste months only to discover they’re offering half of your current salary?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Not-an-Architect Effect</title>
   <link href="https://canro91.github.io/2025/03/28/NotAnArchitect/"/>
   <updated>2025-03-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/28/NotAnArchitect</id>
   <content type="html">&lt;p&gt;Has this also happened to you?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/03/02/SmootherMeetings/&quot;&gt;You’re in a meeting&lt;/a&gt; discussing a technical issue or a bug. After many ideas, you share yours. But it passes unnoticed.&lt;/p&gt;

&lt;p&gt;Since there was no consensus, another meeting is scheduled. But this time with a figure of authority or expertise: an architect, director, or VP.&lt;/p&gt;

&lt;p&gt;Surprisingly enough, they share the same idea as yours. But this time, it’s considered.&lt;/p&gt;

&lt;p&gt;That’s when you say “I said the same thing, but since I’m not an architect, director, or VP nobody listened.” Arrggg!&lt;/p&gt;

&lt;p&gt;Sure, those moments are frustrating. But they earn us points in our authority and trust score.&lt;/p&gt;

&lt;p&gt;It’s not only about sharing good ideas, but how we present them and how others perceive us, just another team member or a trusted one.&lt;/p&gt;

&lt;p&gt;That’s what I call: the not-an-architect effect. A reminder that how we present our ideas is as important as the ideas themselves.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Minifeed Showed Me the Indie Web: My Favorites from Yesterday&apos;s Feed</title>
   <link href="https://canro91.github.io/2025/03/27/IndieWebAndMinifeed/"/>
   <updated>2025-03-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/27/IndieWebAndMinifeed</id>
   <content type="html">&lt;p&gt;If you’re not new to writing online, you’ve heard blogging is dead.&lt;/p&gt;

&lt;p&gt;And there’s some truth behind that. Social media has killed blogging. Google doesn’t like personal blogs anymore. It’s regurgitating AI answers. That made me change &lt;a href=&quot;/2025/01/27/TimesOfDeadBlogging/&quot;&gt;my writing strategy&lt;/a&gt;, by the way. My blog isn’t the only place I show up anymore.&lt;/p&gt;

&lt;p&gt;Personal blogs are still alive…in a small corner of the Internet. The problem is discovering them.&lt;/p&gt;

&lt;p&gt;If Google’s bots don’t crawl and index your blog, your posts will sit there forever, waiting for visitors. That’s not the case with social media. Just open your account and scroll your feed. And boom! Content is there!&lt;/p&gt;

&lt;h2 id=&quot;these-days-while-looking-at-my-analytics-i-discovered-out-a-feed-for-blogs-minifeed&quot;&gt;These days, while looking at my analytics, I discovered out a “feed” for blogs: minifeed&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://minifeed.net/about&quot;&gt;Minifeed&lt;/a&gt; is “a curated blog directory, reader, and search engine.”&lt;/p&gt;

&lt;p&gt;Its goal is to collect personal blogs and make them discoverable. No AI or algorithms behind the feed, just human curation.&lt;/p&gt;

&lt;p&gt;Yesterday, I scrolled through minifeed’s feed and went down the rabbit hole of personal blogs. Here are my favorite posts:&lt;/p&gt;

&lt;p&gt;#1. &lt;a href=&quot;https://maique.eu/posts/2025-03-26-a-big-door&quot;&gt;A photo of a big door&lt;/a&gt;. This is from a photographer running a plog, a photo blog. Did I just invented a new Internet word?&lt;/p&gt;

&lt;p&gt;#2. &lt;a href=&quot;https://sachachua.com/blog/2025/03/feline-feelings/&quot;&gt;A diagram of feline feelings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#3. A software engineer who &lt;a href=&quot;https://arslan.io/2025/03/26/i-was-wrong-about-ai-coding/&quot;&gt;changed his mind about AI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#4. A reflection on &lt;a href=&quot;https://herbertlui.net/the-time-will-pass-anyway/&quot;&gt;time passing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#5. A developer who learned about &lt;a href=&quot;https://hanki.dev/nonograms/&quot;&gt;nonograms&lt;/a&gt;. Imagine a sudoku marrying a crossword, but instead of putting numbers in cells, you have to color them. Definitely, I’m giving them a try.&lt;/p&gt;

&lt;p&gt;#6. A writer who decided to stop writing down ideas because &lt;a href=&quot;https://nik.art/important-ideas-will-come-back/&quot;&gt;good ideas always come back&lt;/a&gt;. Well, I need to write them down to tell my brain “it’s OK, we already did something about it. Back to sleep.” I also discovered another daily blogger.&lt;/p&gt;

&lt;p&gt;#7. A post on &lt;a href=&quot;https://staysaasy.com/saas/2025/03/26/owning-firind.html&quot;&gt;why requests to fire people are often rejected&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#8. Someone using &lt;a href=&quot;https://bobmonsour.com/notes/music-as-a-pain-reliever-post-op/&quot;&gt;music as a pain reliever&lt;/a&gt; after getting his knee replaced.&lt;/p&gt;

&lt;p&gt;#9. And of course, my own post with &lt;a href=&quot;/2025/03/26/ReadCode/&quot;&gt;tips to read code&lt;/a&gt;. Shameless plug.&lt;/p&gt;

&lt;p&gt;If you want to go down the rabbit hole of personal blogs, try &lt;a href=&quot;https://indieblog.page/&quot;&gt;indieblog.page&lt;/a&gt; to get a random post or &lt;a href=&quot;https://searchmysite.net/&quot;&gt;searchmysite.net&lt;/a&gt; for a search engine of personal blogs, &lt;a href=&quot;https://ooh.directory&quot;&gt;ooh.directory&lt;/a&gt;, or &lt;a href=&quot;https://blogroll.org&quot;&gt;blogroll&lt;/a&gt;. Blogging isn’t dead yet.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Three Tips to Read Code More Effectively</title>
   <link href="https://canro91.github.io/2025/03/26/ReadCode/"/>
   <updated>2025-03-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/26/ReadCode</id>
   <content type="html">&lt;p&gt;We spend more time reading than writing code, but we’re seldom taught how to read it.&lt;/p&gt;

&lt;p&gt;Almost 100% of my coding courses in university were about writing code: syntax, algorithms, and class design. Nothing about reading. I only learned about reading code from &lt;a href=&quot;/2022/12/18/LessonsFromExCoworkers/&quot;&gt;a coworker at a past job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yesterday, I found this conversation as part of the GOTO Conference about reading code:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/_R_Vc17mxNE?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Here are some key points from that conversation:&lt;/p&gt;

&lt;h2 id=&quot;1-read-a-piece-of-code-at-first-glance-and-see-what-stands-out&quot;&gt;#1. Read a piece of code at first glance and see what stands out&lt;/h2&gt;

&lt;p&gt;Maybe that’s a function or a symbol or a messy block of code.&lt;/p&gt;

&lt;p&gt;That’s also my go-to guide to refactor a piece of code: &lt;em&gt;While scrolling through a piece of code, if I need to stop and read it twice, there’s something to refactor, something needs to be better explained.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-read-with-empathy&quot;&gt;#2. Read with empathy&lt;/h2&gt;

&lt;p&gt;After reading &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt; for the first time, I wanted every piece of code around me to be perfect.&lt;/p&gt;

&lt;p&gt;No comments. No booleans as parameters. No more than 20 lines per function.&lt;/p&gt;

&lt;p&gt;Every time I found a piece of code that wasn’t closely aligned to the book, I always said, &lt;em&gt;“Who wrote this crap?!”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had to learn that we all do our best with the context and deadlines we have. If we have to fix a pressing issue in 24 hours, we do our best. That might not be the perfect solution.&lt;/p&gt;

&lt;p&gt;And of course, there will always be better ways to solve a problem, once we solve it for the first time.&lt;/p&gt;

&lt;h2 id=&quot;3-always-start-by-pointing-the-good-parts&quot;&gt;#3. Always start by pointing the good parts&lt;/h2&gt;

&lt;p&gt;This is especially when auditing or reviewing other people’s code.&lt;/p&gt;

&lt;p&gt;It reminds me of the principle from How to Win Friends and Influence People: don’t tell anyone they’re wrong.&lt;/p&gt;

&lt;p&gt;Reading code has been one of the most effective ways to improve my coding skills. In fact, I believe it’s &lt;a href=&quot;/2024/12/31/GetBetterAtCoding/&quot;&gt;the best way to learn to code&lt;/a&gt;. That’s why I made it one of the 30 proven strategies in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Simple Script to Keep a Log File with Bash and Vim</title>
   <link href="https://canro91.github.io/2025/03/25/CaptainLog/"/>
   <updated>2025-03-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/25/CaptainLog</id>
   <content type="html">&lt;p&gt;To keep track of my client work, I’ve been using a simple “did” file. You know, &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;I’m a plain-text fan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the end of every workday, I write down what I did for that client and what I need to do the next working day.&lt;/p&gt;

&lt;p&gt;Here’s the script I use—add it to any of your dotfiles:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;did&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    vim +&lt;span class=&quot;s1&quot;&gt;&apos;normal Go&apos;&lt;/span&gt; +&lt;span class=&quot;s1&quot;&gt;&apos;r! date &quot;+\%Y-\%m-\%d \%H:\%M:\%S&quot;&apos;&lt;/span&gt; +&lt;span class=&quot;s1&quot;&gt;&apos;normal o&apos;&lt;/span&gt; ~/did.txt
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From a terminal, simply type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;did&lt;/code&gt;, and it opens a “did.txt” file with &lt;a href=&quot;/2020/09/14/LearnVimForFunAndProfit/&quot;&gt;Vim&lt;/a&gt;, appends the current date and time at the end, and you’re ready to type. Simple as that.&lt;/p&gt;

&lt;p&gt;For everything apart from client work, &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;I’ve ditched my to-do lists&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Ways New Coders Can Use AI Without Generating Code</title>
   <link href="https://canro91.github.io/2025/03/24/NewCodersAndAI/"/>
   <updated>2025-03-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/24/NewCodersAndAI</id>
   <content type="html">&lt;p&gt;If you don’t know how to code, you shouldn’t rely on AI to generate code.&lt;/p&gt;

&lt;p&gt;AI is here to stay. Sure. We can’t ignore it. &lt;a href=&quot;/2025/01/21/AIAdaptation/&quot;&gt;We have to adapt&lt;/a&gt;, like we coders have always done.&lt;/p&gt;

&lt;p&gt;But the problem with AI is when we use it to outsource or replace our thinking. If you’re not the one using the tool, you’re becoming the tool. And tools are easy to replace when a faster and better tool appears.&lt;/p&gt;

&lt;h2 id=&quot;if-youre-a-new-coder-dont-ask-ai-to-generate-code-for-you&quot;&gt;If you’re a new coder, don’t ask AI to generate code for you.&lt;/h2&gt;

&lt;p&gt;Ask AI to do any of these tasks instead:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;Review your code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Dissect a piece of code&lt;/li&gt;
  &lt;li&gt;Explain difficult concepts&lt;/li&gt;
  &lt;li&gt;Create roadmaps and study guides&lt;/li&gt;
  &lt;li&gt;Act as &lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;your debugging rubber duck&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Generate test cases or sample inputs&lt;/li&gt;
  &lt;li&gt;Learn the most common language features&lt;/li&gt;
  &lt;li&gt;Look for security and performance issues&lt;/li&gt;
  &lt;li&gt;Create questionnaires to evaluate yourself&lt;/li&gt;
  &lt;li&gt;Check your understanding of a difficult concept&lt;/li&gt;
  &lt;li&gt;Translate one piece of code to another language&lt;/li&gt;
  &lt;li&gt;Learn the most common methods from standard libraries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;/2025/03/31/AIAndCalculators/&quot;&gt;Using AI is like using calculators in math classes&lt;/a&gt;. They can make you faster if you know what you’re doing, but they can’t think for you.&lt;/p&gt;

&lt;p&gt;As I learned from &lt;a href=&quot;/2024/12/10/JimKwik/&quot;&gt;Jim Kwik, the brain coach&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Use Artificial Intelligence to extend your Human Intelligence, not to replace it.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI can generate code in seconds. But the real value of a coder isn’t in typing. It’s in understanding business problems and collaborating to build the right solutions.&lt;/p&gt;

&lt;p&gt;That’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;, a practical guide to the skills that actually make you a confident coder. It’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>9 Lessons from Writing 10 (Bad) Ideas Daily to Become an Idea Machine</title>
   <link href="https://canro91.github.io/2025/03/23/BecomingAnIdeaMachine/"/>
   <updated>2025-03-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/23/BecomingAnIdeaMachine</id>
   <content type="html">&lt;p&gt;Since October 2024, I’ve written 10 bad ideas every single day and it has transformed my creativity.&lt;/p&gt;

&lt;p&gt;I started this practice inspired by James Altucher. I found it on his blog and books. &lt;a href=&quot;/2025/01/19/ChooseYourselfGuideToWealth/&quot;&gt;Choose Yourself Guide to Wealth&lt;/a&gt; covers it in-depth. He calls it: &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;becoming an idea machine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The point of writing 10 ideas isn’t to come up with 10 perfect ideas, but to exercise your creativity and possibility muscles. The goal is to write 10 &lt;em&gt;bad&lt;/em&gt; ideas about anything every single day.&lt;/p&gt;

&lt;p&gt;Here’s what I’ve noticed after writing thousands of bad ideas in six months:&lt;/p&gt;

&lt;h2 id=&quot;1-you-cant-run-out-of-ideas&quot;&gt;1. You can’t run out of ideas&lt;/h2&gt;

&lt;p&gt;One of my concerns when I started was running out of subjects to write ideas about. But the more I practice it, the more subjects I find to write 10 ideas about.&lt;/p&gt;

&lt;h2 id=&quot;2-a-10-idea-list-could-be-a-post&quot;&gt;2. A 10-idea list could be a post&lt;/h2&gt;

&lt;p&gt;Most of my social media and blog posts start from a 10-idea list. This very post was a 10-idea list.&lt;/p&gt;

&lt;p&gt;Writing 10-idea lists is the first step of &lt;a href=&quot;/2024/12/15/BloggingWorkflow/&quot;&gt;my content wheel&lt;/a&gt;: 10-idea list, then a social media post, then a blog post.&lt;/p&gt;

&lt;h2 id=&quot;3-its-easier-to-execute-ideas-starting-from-10-idea-lists&quot;&gt;3. It’s easier to execute ideas starting from 10-idea lists&lt;/h2&gt;

&lt;p&gt;Starting a big project may feel daunting, with too many moving parts and too much uncertainty.&lt;/p&gt;

&lt;p&gt;But it’s easier starting with 10 bad ideas, then writing another 10 to execute one of them.&lt;/p&gt;

&lt;p&gt;For example, do you want to &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;write a book&lt;/a&gt;? Write 10 subjects you can write a book about. Then, write 10 titles for a book on one of those first 10 subjects. Then, write 10 ideas for chapters. Then, 10 stories to include…and so on and so forth.&lt;/p&gt;

&lt;h2 id=&quot;4-the-first-5-or-6-ideas-are-the-easiest-ones&quot;&gt;4. The first 5 or 6 ideas are the easiest ones&lt;/h2&gt;

&lt;p&gt;The other 4 or 5 are when you start sweating and stretching your idea muscles. After those 4 or 5, that’s when the aha moments happen.&lt;/p&gt;

&lt;h2 id=&quot;5-give-away-10-ideas-to-connect-with-others&quot;&gt;5. Give away 10 ideas to connect with others&lt;/h2&gt;

&lt;p&gt;Instead of reaching out to ask for anything, give away 10 ideas.&lt;/p&gt;

&lt;p&gt;Since I started this practice, my favorite way to connect with someone on LinkedIn (or via email) is giving away 10 post ideas the other person could write. It surprises them and makes the connection more meaningful because you’re giving, not asking.&lt;/p&gt;

&lt;h2 id=&quot;6-writing-10-ideas-works-like-note-taking&quot;&gt;6. Writing 10 ideas works like note-taking&lt;/h2&gt;

&lt;p&gt;After consuming anything, a book or podcast episode, I write 10 ideas I learned from it. It works like taking notes. It helps me recall the main points of what I consumed and remember them more.&lt;/p&gt;

&lt;h2 id=&quot;7-its-a-perfect-exercise-to-quiet-your-mind&quot;&gt;7. It’s a perfect exercise to quiet your mind&lt;/h2&gt;

&lt;p&gt;Write 10 ideas about anything and give your mind a productive task instead of &lt;a href=&quot;/2024/12/19/TimeTravel/&quot;&gt;letting it wander off&lt;/a&gt; imagining lions behind bushes trying to attack you.&lt;/p&gt;

&lt;h2 id=&quot;8-its-a-perfect-replacement-for-a-to-do-list&quot;&gt;8. It’s a perfect replacement for a to-do list&lt;/h2&gt;

&lt;p&gt;Since this year, &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;I’ve ditched my to-do lists&lt;/a&gt; and replaced them with a done list. Guess how I do it? Yes, with a 10-point list.&lt;/p&gt;

&lt;p&gt;At the end of every month, write 10 things you did in that month.&lt;/p&gt;

&lt;h2 id=&quot;9-write-prompts-for-future-days&quot;&gt;9. Write prompts for future days&lt;/h2&gt;

&lt;p&gt;As soon as I have a subject to write 10 ideas about, I write it as a prompt on a new page of my idea pad. Otherwise, I forget about it. It’s easier to sit down and write 10 ideas the next day when you already have a prompt ready.&lt;/p&gt;

&lt;p&gt;Writing 10 ideas a day helped James Altucher get out of bankruptcy and change his life. For me? It has given me lots of ideas for new content. It helped me with &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;my 100-daily-post challenge&lt;/a&gt;. It made me more creative. Because creativity isn’t just about art. It’s also coming up with bad ideas. It’s about becoming an idea machine.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Is Burnout Inevitable in the World of Tech?</title>
   <link href="https://canro91.github.io/2025/03/22/IsBurnoutInevitable/"/>
   <updated>2025-03-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/22/IsBurnoutInevitable</id>
   <content type="html">&lt;p&gt;In 2023, I burned out.&lt;/p&gt;

&lt;p&gt;I checked all the boxes for the first time. The burnout boxes. It wasn’t a good thing. And it took me almost &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;an entire year to recover&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I know I’m not the only one, of course. It’s all over the tech industry.&lt;/p&gt;

&lt;p&gt;In recent weeks, I’ve been exchanging emails with another senior software engineer who went through a burnout season. His take? The tech industry is unsustainable.&lt;/p&gt;

&lt;p&gt;These days, I’ve been chatting with a group of colleagues and friends. Most of them claimed to experience some sort of boredom at their jobs. OK, boredom isn’t burnout, but it’s a warning sign.&lt;/p&gt;

&lt;p&gt;We’re privileged to work in the tech industry.&lt;/p&gt;

&lt;p&gt;But, it has its own challenges—YMMV, of course:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We work long hours.&lt;/li&gt;
  &lt;li&gt;Coding is mentally exhausting.&lt;/li&gt;
  &lt;li&gt;AI threatens us to take our jobs.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;Layoffs are always around the corner&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Agile and SCRUM take control away from us and trap us in “ceremonies.”&lt;/li&gt;
  &lt;li&gt;We’re problem solvers at heart, but often &lt;a href=&quot;/2024/11/18/CodersDontSolveProblems/&quot;&gt;we don’t get to solve problems&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;We’re expected to code after hours just to prove we’re “passionate.” Side projects and open source contributions.&lt;/li&gt;
  &lt;li&gt;We have high standards for quality and development practices. But stakeholders’ expectations don’t align ours. They care about different things.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The toll? Our mental health. Burnout.&lt;/p&gt;

&lt;p&gt;The solution? I don’t have one.&lt;/p&gt;

&lt;p&gt;Yesterday I found on the front page of Hacker News a post about &lt;a href=&quot;https://unionize.fyi/&quot;&gt;tech being a burnout machine&lt;/a&gt; and unionizing as the solution. Mmmm. Dunno.&lt;/p&gt;

&lt;p&gt;My best idea? Detach our sense of meaning from our jobs, recognize &lt;a href=&quot;/2025/01/15/CodingJustForMoney/&quot;&gt;there’s nothing wrong with coding just to pay the bills&lt;/a&gt;, &lt;a href=&quot;/2025/03/03/Boundaries/&quot;&gt;set boundaries between work and non-work&lt;/a&gt;, and &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;diversify our sources of joy&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>You Don&apos;t Need Superpowers to Be a Hero—Save Lives Today!</title>
   <link href="https://canro91.github.io/2025/03/21/SuperBlood/"/>
   <updated>2025-03-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/21/SuperBlood</id>
   <content type="html">&lt;p&gt;You don’t need X-ray vision, super strength, or any other superpower to save lives.&lt;/p&gt;

&lt;p&gt;Recently, I read about &lt;a href=&quot;https://www.npr.org/2025/03/03/nx-s1-5316163/james-harrison-blood-donor&quot;&gt;James Harrison’s story&lt;/a&gt;, an Australian called the “Man with the Golden Arm.” His blood had a rare antibody used to treat a disease in pregnant women. He saved over 2 million babies, his own grandkids included.&lt;/p&gt;

&lt;p&gt;A true hero without any superpowers. Well, a hero with super blood.&lt;/p&gt;

&lt;p&gt;One family member, for health issues, has received blood transfusions at least three times in recent years. She has received 8 “units” in total. This means 4 heroes have saved her life. Since then, I’ve decided to donate enough to at least match the units she has received.&lt;/p&gt;

&lt;p&gt;James was a hero for millions of babies. Some heroes saved my loved one. Inspired by them, I decided to become a hero for someone else too.&lt;/p&gt;

&lt;p&gt;Forget about the red cape and boots, you don’t need them to save lives. Donate blood and be someone’s hero.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Asked Phind and Copilot to Solve an Interview Exercise—Their Solutions Surprised Me</title>
   <link href="https://canro91.github.io/2025/03/20/AISolvingAnExercise/"/>
   <updated>2025-03-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/20/AISolvingAnExercise</id>
   <content type="html">&lt;p&gt;Did AI kill the tech interview?&lt;/p&gt;

&lt;p&gt;Truth is hiring and interviewing have been broken for years. There wasn’t much left to kill. In over 10 years, I’ve witnessed all types of interviews: casual conversations, interrogation-like conversations with rapid-fire questions, &lt;a href=&quot;/2021/11/22/CodingChallengeTips/&quot;&gt;take-home coding exercises&lt;/a&gt;, and the infamous LeetCode exercises.&lt;/p&gt;

&lt;p&gt;I asked &lt;a href=&quot;https://www.phind.com/&quot;&gt;Phind&lt;/a&gt; and &lt;a href=&quot;https://copilot.microsoft.com/&quot;&gt;Copilot&lt;/a&gt; to solve an interview exercise. I might or might not have been asked that exercise when &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;applying to a FAANG&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the “made-up” problem—Wink, wink:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;You’re given a string containing the words “one” and “two”, and the symbols “+” and “-“ representing a math expression. Write a C# function called “Evaluate” to evaluate that expression after replacing “one” with 1 and “two” with 2. Assume the underlying expression is well-formed. For example, “one+one” should return 2, “two-two-one-two” should return -3, and “one+two-one-one+two+one” should return 4.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m using that exact same problem statement as a prompt for both Phind and Copilot. I know…I could have a more advanced prompt.&lt;/p&gt;

&lt;h2 id=&quot;in-this-corner-with-over-10-years-of-c-experience-me&quot;&gt;In this corner, with over 10 years of C# experience, me:&lt;/h2&gt;

&lt;p&gt;Here’s my “pure string evaluation” solution:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;one&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOperation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;+&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;&apos;+&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lastOperation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;+&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lastOperation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOperation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;+&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StartsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastOperation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;+&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;OK, that’s ugly and has lots of repetition. I looped through the string to identify if I was visiting a number or a symbol and stored the last known number and operation.&lt;/p&gt;

&lt;p&gt;Nothing fancy! Probably I wouldn’t have hired myself with that solution. But, hey, “it works on my machine.”&lt;/p&gt;

&lt;p&gt;And, let’s see what Phind and Copilot do. Place your bets…Drum rolls…&lt;/p&gt;

&lt;h2 id=&quot;in-this-corner-with-its-largest-and-most-performant-model-to-date-phiiiiind&quot;&gt;In this corner, with its “largest and most performant model to date,” Phiiiiind:&lt;/h2&gt;

&lt;p&gt;Here’s Phind’s exact solution (here’s &lt;a href=&quot;https://www.phind.com/search/cm8gf90xt0000356sfvlvirfn&quot;&gt;my conversation&lt;/a&gt; but I don’t know how long it’s stored though):&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WordNumberEvaluator&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ONE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TWO&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// Evaluates a mathematical expression containing word numbers &apos;one&apos; and &apos;two&apos;.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;expression&quot;&amp;gt;The expression to evaluate&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;The numerical result of the expression&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Replace word numbers with digits&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;processed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TWO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Use DataTable.Compute to evaluate the expression&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataTable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DataTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordNumberEvaluator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;one+one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;// Outputs: 2&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordNumberEvaluator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;two-two-one-two&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Outputs: -3&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WordNumberEvaluator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;one+two-one-one+two+one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Outputs: 4&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Phind surprised me a lot.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It used a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataTable&lt;/code&gt; to evaluate expressions. I didn’t know &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataTable&lt;/code&gt; could do that. I wouldn’t have thought about that in a real interview.&lt;/li&gt;
  &lt;li&gt;It added docstring comments and two comments to explain its solution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But that didn’t surprise me as much as seeing all the tests turning green when I ran its solution. For a moment, I thought using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataTable&lt;/code&gt; was a hallucination. &lt;a href=&quot;/2025/02/24/AVeteranOnAI/&quot;&gt;A veteran coder warned me about them&lt;/a&gt; the other day.&lt;/p&gt;

&lt;p&gt;If you’re still skeptical, like me when I saw that function working, here’s Microsoft official docs on &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.data.datatable.compute?view=net-8.0&quot;&gt;DataTable’s Compute method&lt;/a&gt;. It’s more Excel-like formulas inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataTable&lt;/code&gt;. The first parameter is an expression and the second one, a filter.&lt;/p&gt;

&lt;p&gt;OK, the thing was Phind referenced 4 sources. Two of them were StackOverflow questions, one of them had the trick with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataTable&lt;/code&gt;. But two of them were Medium posts talking about evaluating math expressions, but nothing inside them related to the solution it used.&lt;/p&gt;

&lt;h2 id=&quot;and-in-this-corner-backed-by-microsoft-copiiiiilot&quot;&gt;And in this corner, backed by Microsoft, Copiiiiilot:&lt;/h2&gt;

&lt;p&gt;Here’s Copilot’s solution:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;one+one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Output: 2&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;two-two-one-two&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Output: -3&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;one+two-one-one+two+one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Output: 4&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Replace &quot;one&quot; and &quot;two&quot; with their numeric equivalents&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Use DataTable to evaluate the expression&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DataTable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DataTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;OK, I used Copilot on a browser, not inside my Visual Studio or Visual Studio Code. Same trick with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataTable&lt;/code&gt;. I wasn’t impressed the second time.&lt;/p&gt;

&lt;p&gt;Not even in my dreams, (and I don’t dream about code) I would have thought about using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataTable&lt;/code&gt; as the trick under my sleeve here, especially with a clock-ticking on a platform like LeetCode or with a hiring manager looking at my screen.&lt;/p&gt;

&lt;p&gt;Did AI kill the tech interview? I’d say yes, at least for data structure and algorithm questions. OK, I only tried it with one coding exercise, but that’s still a yes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>9 Subjects I&apos;ve Changed My Mind About as a Software Engineer</title>
   <link href="https://canro91.github.io/2025/03/19/ChangedMyMind/"/>
   <updated>2025-03-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/19/ChangedMyMind</id>
   <content type="html">&lt;p&gt;Many things have changed over the past 10+ years in my career—technologies, mindsets, and even what I value most from my career.&lt;/p&gt;

&lt;p&gt;I switched from Java to C#. I switched from JavaScript to Xamarin to ASP.NET to ASP.NET Core. From being the “new guy” in my first job to sitting in meetings with the CEO in one of my last jobs. It was a small company, maybe not that impressive.&lt;/p&gt;

&lt;p&gt;Apart from those, here are 9 subjects I’ve changed my mind about:&lt;/p&gt;

&lt;h2 id=&quot;1-databases&quot;&gt;1. Databases&lt;/h2&gt;

&lt;p&gt;I used to hate working with databases.&lt;/p&gt;

&lt;p&gt;I hated them so much that I traded tasks with a coworker at a past job. He did my database tasks in exchange for his API-related tasks.&lt;/p&gt;

&lt;p&gt;Avoiding learning about databases made me take down a server. I wrote a query with &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;a function around a column in a WHERE clause&lt;/a&gt;. Ooops! Don’t do that!&lt;/p&gt;

&lt;p&gt;Everything changed when I decided to learn about databases from scratch, with a beginner’s mindset. My googling took me to &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar and his courses&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Databases and SQL Server aren’t my kryptonite anymore.&lt;/p&gt;

&lt;h2 id=&quot;2-clean-code&quot;&gt;2. Clean Code&lt;/h2&gt;

&lt;p&gt;I read &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt; back in ~2013.&lt;/p&gt;

&lt;p&gt;After that, I became a Clean Code police officer. I looked for Clean Code infractions everywhere around me. I was following and preaching the book.&lt;/p&gt;

&lt;p&gt;Eventually, I learned that not all code is created equal and worth the same. Everything is a tradeoff. And &lt;a href=&quot;/2024/12/25/BestPractices/&quot;&gt;we shouldn’t blindly follow best practices&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-certifications&quot;&gt;3. Certifications&lt;/h2&gt;

&lt;p&gt;“Azure Blah Blah Blah Associate” or “Google Blah Blah Blah Developer.” That sounds impressive.&lt;/p&gt;

&lt;p&gt;When I started coding, I wanted to pile up all those certifications. Until I learned these certifications are often a marketing strategy from the certifying company. Every once in a while, they change the certification tracks and you have to start again. And of course, pay again.&lt;/p&gt;

&lt;p&gt;Instead of certifications to prove my “knowledge,” I opted for just-in-time learning in the context of real projects.&lt;/p&gt;

&lt;p&gt;And if you’re chasing certifications to &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;make your CV look good&lt;/a&gt;, if your CV is in a pile (physically or virtually), you already lost.&lt;/p&gt;

&lt;h2 id=&quot;4-optimizing-everything&quot;&gt;4. Optimizing everything&lt;/h2&gt;

&lt;p&gt;I went from finding small improvements on a random line of code to finding the biggest improvement I could gain with minimal effort, after gathering the right metrics, of course. That’s one of the &lt;a href=&quot;/2025/02/02/LessonsFromBrentOzar/&quot;&gt;lessons I learned from Brent Ozar&lt;/a&gt;. See #1 again.&lt;/p&gt;

&lt;h2 id=&quot;5-climbing-the-corporate-ladder&quot;&gt;5. Climbing the corporate ladder&lt;/h2&gt;

&lt;p&gt;I used to believe I could climb all the way up to the top of the corporate world.&lt;/p&gt;

&lt;p&gt;I was wrong! Anyone can add new steps to that virtual ladder. Anyone can change the entire ladder and you’ll have to start climbing all over again. At any point, someone can say “you’re not ready yet. Keep working hard. Next year.”&lt;/p&gt;

&lt;p&gt;I’ve seen only one successful climb in over 10 years. She joined as an intern and yeeeears later, she became VP of something. &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;It was at my first job&lt;/a&gt;. When I joined, she was already VP. Somebody told me the legend.&lt;/p&gt;

&lt;p&gt;Instead of climbing somebody else’s ladder, I decided to build my own and define my own success metrics.&lt;/p&gt;

&lt;h2 id=&quot;6-working-hard&quot;&gt;6. Working hard&lt;/h2&gt;

&lt;p&gt;“You only do your job and clock out on time,” he said.&lt;/p&gt;

&lt;p&gt;I was on a 1-on-1 with an ex-boss. I don’t remember if he brought up the names of coworkers who worked harder than me. Maybe he implied that in his feedback.&lt;/p&gt;

&lt;p&gt;Funny thing is months later, when the Hunger Games of layoffs began, all those who worked harder were the ones leaving first.&lt;/p&gt;

&lt;p&gt;Apart from working hard, &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;there are other ways to stand out&lt;/a&gt;, of course while being in the right place. Do a good job and clock out on time.&lt;/p&gt;

&lt;h2 id=&quot;7-being-passionate&quot;&gt;7. Being passionate&lt;/h2&gt;

&lt;p&gt;I hate seeing “passionate” anywhere online. On job listings, CVs, and personal blogs.&lt;/p&gt;

&lt;p&gt;What does passionate even mean? Working extra hours, arguing about code style on code reviews, or contributing to open source? I’m not sure anymore.&lt;/p&gt;

&lt;p&gt;For me? All that passion faded away. I learned to &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;diversify my sources of joy&lt;/a&gt; and stopped trying to only find fulfillment from a job.&lt;/p&gt;

&lt;p&gt;I’m not passionate anymore.&lt;/p&gt;

&lt;h2 id=&quot;8-being-a-full-stack-developer&quot;&gt;8. Being a full-stack developer&lt;/h2&gt;

&lt;p&gt;I started writing WebForm apps, then web apps with Bootstrap and Knockout.js, then mobile apps with Xamarin.&lt;/p&gt;

&lt;p&gt;Until I wrote my first REST API with ASP.NET Web API. I didn’t like the “pace” of the front-end ecosystem. Too many frameworks and “something.js” libraries. And too many issues about colors and alignment. It wasn’t my type of thing.&lt;/p&gt;

&lt;p&gt;I stopped trying to become a “full-stack” developer. Often, that’s an excuse to hire fewer people and overwork others and pay low salaries. Sorry, but not sorry.&lt;/p&gt;

&lt;p&gt;Mmmm…Well, now that &lt;a href=&quot;/2025/02/24/AVeteranOnAI/&quot;&gt;AI is “taking” our jobs&lt;/a&gt;, maybe we all have to become full-stack developers. And maybe I’ll change my mind about this and write anoter post.&lt;/p&gt;

&lt;h2 id=&quot;9-being-the-best-coder&quot;&gt;9. Being the best coder&lt;/h2&gt;

&lt;p&gt;I don’t want to be the best coder on a team anymore.&lt;/p&gt;

&lt;p&gt;I believed cracking symbols on a page was the way to go. I’ve learned that &lt;a href=&quot;/2024/12/01/WhoGetsPromoted/&quot;&gt;the best coders stay coding&lt;/a&gt;. And there’s more than symbols on a file. The hardest part of software engineering isn’t the code, but “people and interactions.” And that’s where the real growth happens.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Day in the Life of a Random C# Backend Engineer</title>
   <link href="https://canro91.github.io/2025/03/18/ADayInTheLifeOfACoder/"/>
   <updated>2025-03-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/18/ADayInTheLifeOfACoder</id>
   <content type="html">&lt;p&gt;What exactly does a C# backend software engineer do?&lt;/p&gt;

&lt;p&gt;These days, I found this question on &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1j4u01j/what_kind_of_tasks_do_you_usually_work_on/?rdt=40341&quot;&gt;Reddit&lt;/a&gt; about the type of tasks we C# developers do:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Do you find them exciting or just routine? Do they help you grow as a developer? Or is it mostly about finishing tasks quickly, making small adjustments, and moving on? Curious to hear your thoughts!”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;its-845-time-for-the-daily-meeting&quot;&gt;It’s 8:45. Time for the daily meeting.&lt;/h2&gt;

&lt;p&gt;You join the meeting. &lt;em&gt;“Good morning.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then you look through the window, scroll on Hacker News, and check your phone while you wait for your name.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Yesterday, I worked on the task to yada, yada. No blockers.”&lt;/em&gt; Yes, another meeting that could be an update on Teams or Slack or an email.&lt;/p&gt;

&lt;h2 id=&quot;its-915-time-for-a-coffee&quot;&gt;It’s 9:15. Time for a coffee.&lt;/h2&gt;

&lt;p&gt;You pick your next ticket.&lt;/p&gt;

&lt;p&gt;Another pair of REST endpoints to power a screen on the web application. You come up with request and response objects. And you contact the front-end developer to share those two objects, so nobody is blocked while the endpoints are ready.&lt;/p&gt;

&lt;p&gt;You come up with validations, entities, and &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;value objects&lt;/a&gt; to populate your core domain. Yes, &lt;a href=&quot;/2025/03/16/DDDIsNotAboutEntities/&quot;&gt;you’re kind of doing DDD&lt;/a&gt;. &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Write some tests&lt;/a&gt;. Fire up your Postman and see if everything works.&lt;/p&gt;

&lt;p&gt;That was the writing part.&lt;/p&gt;

&lt;p&gt;Now, the reading part. The web application uses a grid with dynamic filters and ordering. It seems like a task for &lt;a href=&quot;/2021/03/08/HowNotToWriteDynamicSQL/&quot;&gt;dynamic SQL&lt;/a&gt;. But you’re too lazy to write SQL queries by hand. It’s 2025 and the world has ORMs. Life is too short to write SQL queries by hand.&lt;/p&gt;

&lt;p&gt;You need to pass a list of IDs to a query. You could use a string and send the IDs separated by commas. Passing a table type is the solution. But…Surprise, surprise, your ORM doesn’t seem to support tables as parameters.&lt;/p&gt;

&lt;h2 id=&quot;noon-its-lunchtime&quot;&gt;Noon. It’s lunchtime.&lt;/h2&gt;

&lt;p&gt;You step away from the screen to grab some food and give your brain a rest. At least, that’s the plan. But you can’t stop thinking about your task. &lt;em&gt;“Why it isn’t working? What am I missing?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you’re back from lunch, you pull down the source code of your ORM and go down a rabbit hole. There’s no way you’re writing SQL queries by hand. Not today. &lt;em&gt;“Here it builds the SQL query from the mapping object…“&lt;/em&gt; Great. Progress. But still nothing. You keep going down the rabbit hole, determined to find the answer.&lt;/p&gt;

&lt;p&gt;One method leads to another. One break point here and there. Until finally, you find it. &lt;em&gt;“Here it is. This ORM uses a converter to populate the underlying DbCommand object. I could use that.”&lt;/em&gt; The solution is kind of hacky, but it works.&lt;/p&gt;

&lt;p&gt;You write a converter and a simple test to prove you’re right. Then you port that converter to your Database project and add some docstring comments to document your hacky solution.&lt;/p&gt;

&lt;h2 id=&quot;its-almost-500-pm&quot;&gt;It’s almost 5:00 PM.&lt;/h2&gt;

&lt;p&gt;You’re tired from that &lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;debugging session&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But it was rewarding. You proved that sometimes being lazy means being efficient. By the end of your debugging session, you know a bit more about the internals of your obscure ORM. You have a trick you’ll use in future tasks. And you have an idea for &lt;a href=&quot;/2025/03/11/BloggingTips/&quot;&gt;a good blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s time to create a pull request.&lt;/p&gt;

&lt;p&gt;You slap a title, a description, and some good comments here and there to explain what you found. You change your ticket status on JIRA and ping the default reviewer for this project.&lt;/p&gt;

&lt;p&gt;Time to unplug from work.&lt;/p&gt;

&lt;h2 id=&quot;its-845-the-next-day-another-daily-meeting&quot;&gt;It’s 8:45 the next day. Another daily meeting.&lt;/h2&gt;

&lt;p&gt;This time, you don’t scroll Hacker News, but go directly to your PR.&lt;/p&gt;

&lt;p&gt;As usual, one reviewer is suggesting renaming a variable. And you forgot to cover one or two lines. Yes, the official guideline is 100% coverage. Arrggg!&lt;/p&gt;

&lt;p&gt;Some of your coworkers find out the trick with the converter. For the first time in a while, you get a compliment in one of your PRs. Yay!&lt;/p&gt;

&lt;p&gt;That’s just one ticket. There are more. Hopefully QA doesn’t find any issues in the search logic. Fingers crossed, all the issues are “move this label,” “this textbox isn’t green enough… .” Nothing you have to care about being a backend developer. Your UI is Postman, and everything is working fine from there.&lt;/p&gt;

&lt;p&gt;Everything because you didn’t want to write SQL queries by hand. Lazy? Maybe. Clever? Absolutely.&lt;/p&gt;

&lt;p&gt;That’s why you chose backend development: the satisfaction of solving tricky problems. And that’s just another day in the life of a random backend engineer. Based on true events, because backend development could be boring, and yes, sometimes, a little wild.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>For Any Creative Project, Volume Beats Perfection</title>
   <link href="https://canro91.github.io/2025/03/17/VolumeWins/"/>
   <updated>2025-03-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/17/VolumeWins</id>
   <content type="html">&lt;p&gt;Volume always wins.&lt;/p&gt;

&lt;p&gt;When we start a new creative project (writing, coding, or painting), we aim to create something “perfect” on our first attempts. And we wait for perfection before we share our work with the world.&lt;/p&gt;

&lt;p&gt;The truth is, those first attempts will be crap. Our first posts, programs, and paintings will be crap. Well, &lt;a href=&quot;/2025/01/02/SturgeonLaw/&quot;&gt;90% of everything is crap&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But the more we practice, the better we become, and the closer to “perfect” we get.&lt;/p&gt;

&lt;p&gt;That’s a lesson I learned when binge-reading &lt;a href=&quot;/2024/09/16/LessonsFromHerbertLuiBlog/&quot;&gt;Herbert Lui’s blog&lt;/a&gt; the other day. I read about the pottery class experiment. One half of a pottery class was graded on quality and the other half on quantity. At the end of the course, the half who went for quantity did better and got better grades. They focused on experimenting, learning, and improving along the way.&lt;/p&gt;

&lt;p&gt;The more tries we do:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;We experiment more&lt;/li&gt;
  &lt;li&gt;We become more confident&lt;/li&gt;
  &lt;li&gt;We collect more data points&lt;/li&gt;
  &lt;li&gt;We build a larger body of work&lt;/li&gt;
  &lt;li&gt;We have more chances to succeed&lt;/li&gt;
  &lt;li&gt;We understand our audience better&lt;/li&gt;
  &lt;li&gt;We develop less aversion to failure&lt;/li&gt;
  &lt;li&gt;We have more material to repurpose&lt;/li&gt;
  &lt;li&gt;We gather more opportunities for feedback&lt;/li&gt;
  &lt;li&gt;We create more points of contact with our audience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For your next creative project, go for volume: &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;write 100 blog posts&lt;/a&gt;, write 100 short programs, or do 100 paintings. And don’t be afraid of &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;showing your work&lt;/a&gt;. The more you create, the closer you get to “perfect.” Volume always wins.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Domain-Driven Design Isn&apos;t Just About Entities and Value Objects</title>
   <link href="https://canro91.github.io/2025/03/16/DDDIsNotAboutEntities/"/>
   <updated>2025-03-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/16/DDDIsNotAboutEntities</id>
   <content type="html">&lt;p&gt;What’s the first thing that comes to mind when you think of Domain-Driven Design (DDD)? Entities and value objects?&lt;/p&gt;

&lt;p&gt;These days, I came across this question on &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1jaq72n/what_are_your_thoughts_on_ddd/&quot;&gt;Reddit&lt;/a&gt; about DDD:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I’ve been struggling lately to understand the idea and the reason behind Domain Driven Design. However, I have come up with the understanding that DDD is just the implementation of the core domain in Rich-Domain models, creating a ubiquitous language to make technical development easier, bounded contexts and design patterns like aggregates, value objects, strongly typed IDs and so on.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s easy to confuse DDD with using entities and &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;value objects&lt;/a&gt;. But that’s not the point.&lt;/p&gt;

&lt;p&gt;At a past job, I had the chance to use DDD in small projects. Every two months or so, we started short projects to improve a hotel management software.&lt;/p&gt;

&lt;h2 id=&quot;in-most-of-those-projects-we-used-and-abused-ddd&quot;&gt;In most of those projects we used, and abused, DDD.&lt;/h2&gt;

&lt;p&gt;Here are two examples where we abused DDD:&lt;/p&gt;

&lt;p&gt;#1. We used it on a CRUD application to text guests upon arrival. The project got behind schedule, in part due to forcing DDD on a project with a tight schedule. From that project, &lt;a href=&quot;/2022/12/17/LessonsOnAFailedProject/&quot;&gt;I learned some good lessons&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#2. In another project, slightly more complex, but still not business logic heavy, we followed DDD by the book. Almost literally we followed &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;this book&lt;/a&gt;. We ended up with &lt;a href=&quot;/2023/08/07/TooManyLayers/&quot;&gt;too many layers and mappings between layers&lt;/a&gt; just to read data from the database. Arrggg!&lt;/p&gt;

&lt;p&gt;If we judge DDD by using entities, value objects, and aggregate roots, in these two examples, we did DDD. But we overengineered a solution that we could have simply done with an n-tier architecture or commands and queries.&lt;/p&gt;

&lt;h2 id=&quot;ddd-isnt-about-aggregate-roots-entities-and-value-objects&quot;&gt;DDD isn’t about aggregate roots, entities, and value objects.&lt;/h2&gt;

&lt;p&gt;DDD is about collaboration and understanding.&lt;/p&gt;

&lt;p&gt;It’s about collaboration between stakeholders and developers to create a shared understanding of the business rules. And it’s about expressing that understanding in a shared language. From product people in user stories to developers, all the way through the code, and up to table and column names.&lt;/p&gt;

&lt;p&gt;Aggregate roots, entities, and value objects are mechanisms to encapsulate that understanding inside boundaries in the code.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We Write Code for Two Audiences—with Two Different Priorities</title>
   <link href="https://canro91.github.io/2025/03/15/CodingForTwoAudiences/"/>
   <updated>2025-03-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/15/CodingForTwoAudiences</id>
   <content type="html">&lt;p&gt;“[Users] don’t care about your code—they care about results.”&lt;/p&gt;

&lt;p&gt;That’s a controversial statement I found on &lt;a href=&quot;https://dev.to/wraith/building-for-users-the-real-purpose-of-software-development-a3a&quot;&gt;this dev.to post&lt;/a&gt;. It got some virtual stones in the comments from “Clean Code” police officers. I used to be one too. &lt;a href=&quot;/2025/03/19/ChangedMyMind/&quot;&gt;I’ve changed my mind about it&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;we-dont-write-code-for-the-sake-of-writing-code&quot;&gt;We don’t write code for the sake of writing code.&lt;/h2&gt;

&lt;p&gt;We write code to accomplish something for others:&lt;/p&gt;

&lt;p&gt;Connecting them with loved ones in the case of Facebook, finding a romantic partner on Tinder, or getting a good and cheap room on Airbnb. Even for boring enterprise software, it’s about happier clients and more sales.&lt;/p&gt;

&lt;p&gt;That’s what matters most to the users of Facebook, Tinder, Airbnb, and even boring enterprise software.&lt;/p&gt;

&lt;p&gt;The other day, the VP of a software company I was contracting with taught me a lesson about &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt; and &lt;a href=&quot;/2024/12/25/BestPractices/&quot;&gt;other best practices&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Clean Code is for us when we have to fix problems and deal with our own mess…users don’t care about that.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was a lesson I won’t forget, especially coming from a VP of a software company who had been a coder too.&lt;/p&gt;

&lt;h2 id=&quot;we-write-code-for-humans-for-two-audiences-our-future-selves-and-end-users&quot;&gt;We write code for humans. For two audiences: our future selves and end users.&lt;/h2&gt;

&lt;p&gt;Both audiences don’t necessarily intersect. Both audiences don’t care about the same things.&lt;/p&gt;

&lt;p&gt;For us and our future selves, it’s about maintaining code quality to add new features and solve bugs.&lt;/p&gt;

&lt;p&gt;For end users, connecting with loved ones, finding a romantic partner, getting a cheap room, and keeping clients happy and sales high. They care about what our code could do for them, not about the code by itself. Quality is there to support that mission.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Know When It&apos;s Time to Leave a Job</title>
   <link href="https://canro91.github.io/2025/03/14/SignsToLeave/"/>
   <updated>2025-03-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/14/SignsToLeave</id>
   <content type="html">&lt;p&gt;I’ve stayed too long at stagnant jobs, and I regret it.&lt;/p&gt;

&lt;p&gt;Staying too long at jobs cost me years and thousands of dollars. I left lots of money on the table. It made some of my skills get rusty.&lt;/p&gt;

&lt;p&gt;But after connecting the dots, there were signs I failed to notice:&lt;/p&gt;

&lt;h2 id=&quot;1-youre-not-learning-anything-new&quot;&gt;1. You’re not learning anything new&lt;/h2&gt;

&lt;p&gt;This is the #1 sign to look for. If your daily job becomes boring, that’s a sign. Or stay but &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;diversify your source of joy&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-theres-no-room-for-growth&quot;&gt;2. There’s no room for growth&lt;/h2&gt;

&lt;p&gt;“You can build any role you want,” I was told.&lt;/p&gt;

&lt;p&gt;That happened at a past job. I rejected the idea of &lt;a href=&quot;/2025/01/08/BeingATeamLeader/&quot;&gt;being a team leader&lt;/a&gt;. I didn’t like how the role was designed at that place. It was a “wear all hats for the same pay” role.&lt;/p&gt;

&lt;p&gt;And when I brought my ideas to the table, they got rejected with a “somebody else is already kind of doing that.” Staff Engineer? Software Architect? Right Hand? All of them.&lt;/p&gt;

&lt;p&gt;It was a red flag I failed to notice.&lt;/p&gt;

&lt;h2 id=&quot;3-it-makes-you-sick&quot;&gt;3. It makes you sick&lt;/h2&gt;

&lt;p&gt;If your job makes you sick, physically or mentally, what other sign do you need? It’s time to take action. It’s time to run. Run, Forrest, run!&lt;/p&gt;

&lt;p&gt;You can always get a new job, but not a new body.&lt;/p&gt;

&lt;h2 id=&quot;4-youre-learning-coping-mechanisms&quot;&gt;4. You’re learning coping mechanisms&lt;/h2&gt;

&lt;p&gt;“OK, I just sent a message without a period at the end and without capitalizing the first letter,” my friend told me.&lt;/p&gt;

&lt;p&gt;He was &lt;a href=&quot;/2025/03/03/Boundaries/&quot;&gt;working from home&lt;/a&gt; and I stopped by for a coffee. In the middle of our conversation, he got a message from work and replied from his phone. There’s nothing wrong with that.&lt;/p&gt;

&lt;p&gt;But to make sure his boss didn’t notice it, he didn’t add a period at the end of his message. Oh boy! I had just left that job no more than one year before that.&lt;/p&gt;

&lt;h2 id=&quot;5-theres-no-more-money&quot;&gt;5. There’s no more money&lt;/h2&gt;

&lt;p&gt;The last time I heard “There’s no more money this year” when asking for a raise, the Hunger Games of layoffs started, until only a few ones were left standing.&lt;/p&gt;

&lt;p&gt;I tried to convince myself I could stretch that job for another year or two. Wrooong! Eventually, &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;layoffs knocked at my door too&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;6-you-have-a-bad-boss&quot;&gt;6. You have a bad boss&lt;/h2&gt;

&lt;p&gt;Often it’s hard to leave work stuff at work when going back home. A bad boss (whatever that means to you) will make you feel bad along with the people around you.&lt;/p&gt;

&lt;h2 id=&quot;7-theres-no-willingness-to-change&quot;&gt;7. There’s no willingness to change&lt;/h2&gt;

&lt;p&gt;If after raising flags with your boss, you only hear “that’s fine. It used to be worse” or anything along those lines, that’s where change dies. Don’t waste more time bringing ideas.&lt;/p&gt;

&lt;p&gt;Either you accept things the way they are or disagree with your feet.&lt;/p&gt;

&lt;h2 id=&quot;8-its-more-important-to-find-who-to-blame&quot;&gt;8. It’s more important to find who to blame&lt;/h2&gt;

&lt;p&gt;If something goes wrong, it means there’s a flaw in a standard procedure or a process that let it happen. Finding who to blame only create a toxic work environment. Don’t waste time blaming. Fix the process.&lt;/p&gt;

&lt;p&gt;From &lt;a href=&quot;/2025/02/02/LessonsFromBrentOzar/&quot;&gt;Brent Ozar&lt;/a&gt;, the SQL Server master, I learned that by the time we ask ourselves if we should leave, it’s too late. &lt;a href=&quot;/2024/11/17/BestTimeToLookForANewJob/&quot;&gt;The best time to look for a job&lt;/a&gt; isn’t when you’re desperate. It’s when you don’t need one. Start &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;updating your CV&lt;/a&gt;, reflect on what you truly want, and take control of your career. And don’t forget to &lt;a href=&quot;/2024/12/04/RedFlags/&quot;&gt;read the fine print in job descriptions&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What I Learned From Watching Netflix&apos;s Six Triple Eight</title>
   <link href="https://canro91.github.io/2025/03/13/SixTripleEight/"/>
   <updated>2025-03-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/13/SixTripleEight</id>
   <content type="html">&lt;p&gt;If we look at the world as creators, &lt;a href=&quot;/2025/01/26/ContentIsEverywhere/&quot;&gt;there’s content everywhere&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I put on my creator’s glasses and watched Six Triple Eight, a Netflix movie about the only battalion of American Black women in Europe during WWII.&lt;/p&gt;

&lt;p&gt;Here are the storytelling devices I noticed:&lt;/p&gt;

&lt;p&gt;#1. The story starts somewhere in the middle, not at the beginning of the events. It starts with a scene of a plane being shot down in the middle of WWII. Then, it takes us back to the days before that incident and builds from there.&lt;/p&gt;

&lt;p&gt;#2. This battalion ran the mail operation for the troops in the frontlines. So we follow a letter covered with blood from the first scene all the way throughout the movie.&lt;/p&gt;

&lt;p&gt;#3. Apart from delivering letters, it tells us other stories:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The story of Major Adams, the officer in charge of the battalion. Based on a real character.&lt;/li&gt;
  &lt;li&gt;The discrimination Black women faced inside the Army.&lt;/li&gt;
  &lt;li&gt;Lena, our protagonist, falling in love with the pilot from the first scene.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;#4. It follows the problem/tension/climax/resolution technique:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It starts with the battalion facing an impossible mission (delivering the mail)&lt;/li&gt;
  &lt;li&gt;Being Black women in the Army, nobody believed in them.&lt;/li&gt;
  &lt;li&gt;They completed the mission ahead of time.&lt;/li&gt;
  &lt;li&gt;They earned the respect of the people who used to bully them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;#5. The entire story is told from the point of view of a single woman, Lena, our protagonist. Based on a real character too.&lt;/p&gt;

&lt;p&gt;#6. Like any other movie inspired by real events, it wraps up with the real footage of the real Six Triple Eight battalion entering in Europe.&lt;/p&gt;

&lt;p&gt;#7. The movie starts with a love story and ends with another love story. It ends the same way it started.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Caching Trolls You When Serving Images With Unchanged URLs</title>
   <link href="https://canro91.github.io/2025/03/12/CachingImages/"/>
   <updated>2025-03-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/12/CachingImages</id>
   <content type="html">&lt;p&gt;Oh, boy! It took me like 3 hours to figure this out.&lt;/p&gt;

&lt;p&gt;Here’s the thing: I had a CRUD app with Blazor. In the Update page, I could change images—let’s say, a hinge and other parts used to build a closet. Nothing fancy.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem?&lt;/h2&gt;

&lt;p&gt;After updating the image and redirecting back to the List page, it still showed the previous image. Whaaat?! The API endpoint did update the image.&lt;/p&gt;

&lt;p&gt;Since I can’t show you the real code, for obvious reasons, here’s a little corner of the app,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2025-03-12-CachingImages/CachingImages.gif&quot; alt=&quot;Caching images&quot; width=&quot;900px&quot; /&gt;
    &lt;figcaption&gt;After updating my image, it still showed the old one. Arrggg!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It only worked after pressing F5, to reload the entire page.&lt;/p&gt;

&lt;p&gt;I tried to solve it by forcing a reload when redirecting from the Update page back to the List page. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnSaveClicked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBrowserFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyHttpService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpPut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomePutResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;api/my-cool-endpoint&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_someUpdateRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyHttpService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpPostFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeImageResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;api/another-cool-endpoint&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;NavManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NavigateTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forceLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                                 ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But sometimes it still showed the old image. Arrggg!&lt;/p&gt;

&lt;p&gt;After googling for a while, &lt;a href=&quot;https://stackoverflow.com/questions/4495316/how-do-i-clear-the-image-cache-after-editing-an-image-on-my-site&quot;&gt;this SO answer&lt;/a&gt; saved my day. It was the browser trolling me all that time.&lt;/p&gt;

&lt;p&gt;OK, the API endpoint powering the List page returned a public URL of every image, served from the same server. Every image file was named after the related object ID, something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/public-files/acme-corp/my-cool-object/123.png&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Even though the backend changed the images, their URLs were the same. And the browser didn’t notice anything new, so it kept showing the same image, without requesting it from the server.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The solution?&lt;/h2&gt;

&lt;p&gt;Append a timestamp to every URL. &lt;a href=&quot;/2022/12/11/AuditFieldsWithOrmLite/&quot;&gt;Audit fields are helpful here&lt;/a&gt;…I ended up appending the modification date of every image. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPublicUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;publicUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;/files/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_companyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modificationAt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetLastModificationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_someBasePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publicUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;?ts=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modificationAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HHmmssfff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetLastModificationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;absolutePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;absolutePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastWriteTimeUtc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Kudos to StackOverflow. Caching trolled me. Again. Total facepalm!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Tips to Write Posts (Technical or Not) People Can&apos;t Stop Reading</title>
   <link href="https://canro91.github.io/2025/03/11/BloggingTips/"/>
   <updated>2025-03-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/11/BloggingTips</id>
   <content type="html">&lt;p&gt;I’ve written 321 posts.&lt;/p&gt;

&lt;p&gt;In fact, this is my post number 322. Over five years ago, &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;I wrote my first post&lt;/a&gt;. Well, “post” is a strong word. It was a word vomit. I still keep my first post unedited to remind me how I started.&lt;/p&gt;

&lt;p&gt;I only wrote when I thought I had something to say. It was once every full moon when I started. Then it was every other week. Since &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;Nov 1st, 2024&lt;/a&gt;, I’ve been doing it daily. I already passed the &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;100 daily posts mark&lt;/a&gt;. My next goal: the 200 mark.&lt;/p&gt;

&lt;p&gt;My blog is &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;my time capsule&lt;/a&gt;. And it’s &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;done more for me than a portfolio&lt;/a&gt;. These days, it’s part of &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;my routine to be healthy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All that to say, I’ve learned a thing or two about blogging and writing. Mostly, after lots of trial and error. And if you’re starting out, here are 10 quick tips for you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Write descriptive headlines&lt;/strong&gt;: “How to do X with Y in order to Z” works perfectly fine for most of your posts, technical or not. Or &lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;steal your headlines&lt;/a&gt;, like an artist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. Ditch long intros&lt;/strong&gt;: Go straight to your main point. Don’t worry about introductions at the beginning. I wanted to jump straight to #1 in this post. But in a moment of inspiration, I wrote that introduction to “prove” I know what I’m talking about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Start with a 1-sentence paragraph&lt;/strong&gt;: OK, if you decide to write introductions, start your posts with one sentence. Imagine your first line is like an extra headline. See what I did in this post. I learned this trick from &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;my first writing class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Use subheads&lt;/strong&gt;: Use h2 and h3 tags to break your post into parts. These tags help readers skim and digest your post. And this helps SEO bots understand your post. If you’re writing a how-to guide, use one subhead for every step/lesson/point of your post…You know what, I could have used one h2 tag for every point in this post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Write shorter paragraphs&lt;/strong&gt;: Shorter paragraphs and sentences make your posts easy to digest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Use simple words&lt;/strong&gt;: A post isn’t a New York Times column or a school essay where you’re chasing an A+ or a 10.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Deliver your message fast&lt;/strong&gt;: It goes hand in hand with #2. Your headline is a promise. Fulfill your promise in your post body. If you’re writing how to fix the air conditioning of your car, tell that in your post. Don’t go on tangents. Leave those tangents to &lt;a href=&quot;/2025/01/28/DoubleYourPostCount/&quot;&gt;write more posts&lt;/a&gt;. Give something and give it fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Avoid meta sentences&lt;/strong&gt;: I blame writing in schools and their mantra: “tell readers what you’re going to tell them, tell the thing, then tell what you just told them.” Arrggg! Don’t do that. Don’t write “now, let’s cover how to…” or anything like that. I used to do that after every section. See #7.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Write for 1 person&lt;/strong&gt;: Next time you sit down to write, picture yourself &lt;a href=&quot;/2024/12/24/WritingVoice/&quot;&gt;explaining it to only one person&lt;/a&gt;: a friend, a coworker, or even your dog. It will give you the right tone to use and the right points to cover in your post. For example, again if you’re writing about how to fix the air conditioning of your friend’s car, you don’t have to explain what a car is to him. He already knows it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Avoid summary-like conclusions&lt;/strong&gt;: Ditch conclusions the same way you ditch intros. And please avoid regurgitating everything back in a conclusion. Instead, ask your readers to do something after reading your post or promote other posts. Again see #2, #7, and #8.&lt;/p&gt;

&lt;p&gt;I could give you more tips on SEO, but let’s keep it like that. If you followed these tips, especially #1 and #4, search engines will like you. Search engines love when we write for humans.&lt;/p&gt;

&lt;p&gt;Keeping a blog helped me skip hiring lines the last time I was looking for a job. I showed my blog during the interview and the process went faster from there. No more rounds of interviews after that.&lt;/p&gt;

&lt;p&gt;You don’t have to be an expert to write. Write to become one. But, please &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;don’t start by writing your own blogging engine&lt;/a&gt;. That’s where blogging goes to die.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Always Automate Code Style (and Other Best Practices)</title>
   <link href="https://canro91.github.io/2025/03/10/AutomateCodeStyle/"/>
   <updated>2025-03-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/10/AutomateCodeStyle</id>
   <content type="html">&lt;p&gt;At one of my previous jobs, there was a new guideline:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Format all SQL queries using right-aligned style.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem?&lt;/h2&gt;

&lt;p&gt;During a &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;pull request review&lt;/a&gt;, one or two managers, who didn’t code anymore, decided on that guideline.&lt;/p&gt;

&lt;p&gt;And the next day, without telling anyone else, that guideline was set in stone and enforced throughout the codebase. Guess what was the most common comment on code reviews since then? “Please properly format this SQL query.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Always share new guidelines and coding style changes with the team, especially if you’re planning on enforcing them on all projects. Just send an email or a quick Slack update to make sure everybody is on the same page. No need for an all-hands meeting.&lt;/p&gt;

&lt;h2 id=&quot;the-other-problem&quot;&gt;The other problem?&lt;/h2&gt;

&lt;p&gt;There was no clear way to format those files. Should every coder do it by hand? Using their favorite tool? Or using an “official” tool?&lt;/p&gt;

&lt;p&gt;Being a lazy coder, I didn’t want to format my files by hand. Copying SQL queries from Visual Studio, going to a tool page, pasting them, and copying them back was too much effort—I told you I’m lazy.&lt;/p&gt;

&lt;p&gt;Computers are designed to do boring and repetitive work. Formatting files definitely fit that description. In one afternoon of frustration, I hacked &lt;a href=&quot;/2023/09/18/FormatSqlFilesOnCommit/&quot;&gt;a Git hook to format my SQL files&lt;/a&gt; and shared it with my team lead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Make sure to have an automated and widespread tool so anyone can follow new guidelines and coding style changes as easily as possible.&lt;/p&gt;

&lt;p&gt;We wasted so much time on code reviews, asking people to format those files, &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;waiting 24 hours to finally merge things&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For better or worse, my next project at that job didn’t involve SQL. And months later, &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;I was let go&lt;/a&gt;…so I don’t know what happened to that guideline or to my tool.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>9 Must-Know Tips for Your Next Trip or Vacation</title>
   <link href="https://canro91.github.io/2025/03/09/TravelTips/"/>
   <updated>2025-03-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/09/TravelTips</id>
   <content type="html">&lt;p&gt;I’m far from collecting enough passport stamps for a world tour.&lt;/p&gt;

&lt;p&gt;But, I’ve traveled abroad more than once. &lt;a href=&quot;/2025/02/04/MakingMoneyCoding/&quot;&gt;Thanks to coding&lt;/a&gt;, I saved enough to visit Spain and France before the pandemic. It was my final exam after &lt;a href=&quot;/2020/10/23/ThreeLanguageLessons/&quot;&gt;studying French&lt;/a&gt; for a couple of years.&lt;/p&gt;

&lt;p&gt;Here are 9 tips I’ve learned from those travels:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Always pack a jacket&lt;/strong&gt;: Last time I was on vacation I visited a tropical country. Of course, I only packed shorts and swimsuits. But once I was thousands of feet in the air and inside a pressurized cabin, I was freezing.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Pack underwear and cleaning essentials in your handbag&lt;/strong&gt;: In case, your baggage is lost or delayed. Better safe than sorry.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Visit your favorite destination first&lt;/strong&gt;: For my travel to France, Paris was the last city I visited. By the time I got there, I didn’t have the energy to walk for hours and visit museums. I missed a lot of must-see places. And I didn’t truly enjoy the ones I visited. I was tired. Next time, I’m visiting my favorite destinations first.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Schedule a free day&lt;/strong&gt; to wander around, sit in a park, go to a cafe, or simply do nothing. Often, we travel to rest and unplug but we end up exhausted from hours of walking, connecting flights, and museums.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Start with a walking tour&lt;/strong&gt;: Often, they’re free. Or you can leave a tip at the end of the tour. And you have the chance to ask your tour guide for places to eat and hang around where locals go.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Bring eye masks and earplugs.&lt;/strong&gt; Perfect for sleeping on planes or trains…And hostels can get loud sometimes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;If you’re crossing the Atlantic, from America to Europe, start your flight in the evening&lt;/strong&gt;: This lets you get some sleep on the plane and arrive at your destination with daylight. It reduces the jet lag.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Wear pants that don’t need a belt and shoes without laces&lt;/strong&gt;. It will save you a lot of time at security controls.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Keep your passport in your bag and only take it out at security checkpoints&lt;/strong&gt;. Often there’s some stigma associated with our country of origin. I bet the first thing that comes to mind when you think of Colombia isn’t Encanto, just like Coco when you think of Mexico—both Disney movies. Keeping your passport in plain sight invites extra scrutiny from security personnel walking around airports.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Tips Every New Coder Should Know to Succeed</title>
   <link href="https://canro91.github.io/2025/03/08/TipsForNewCoders/"/>
   <updated>2025-03-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/08/TipsForNewCoders</id>
   <content type="html">&lt;p&gt;I remember the first day of my first coding job.&lt;/p&gt;

&lt;p&gt;It was over 10 years ago. I wore a long-sleeved shirt and fancy shoes. I brought a CD to install Visual Studio 2010. Yes, a CD. And yes, Visual Studio 2010.&lt;/p&gt;

&lt;p&gt;After getting my corner and shaking hands, someone handed me a book. It was a book about SQL Server 2008. I read and googled for a couple of weeks until I got my first task. I was coming from Java and I needed to know C# in a few days.&lt;/p&gt;

&lt;p&gt;My first task was to port a legacy ASP.NET WebForms app into a desktop app. I wrote Java code with C# keywords.&lt;/p&gt;

&lt;p&gt;I had to figure out lots of things. Coding was the easy part. Everything else was the difficult part.&lt;/p&gt;

&lt;p&gt;If you’re like me over 10 years ago, here are 10 tips you should know to succeed as a new coder—this is what I wish I knew back then:&lt;/p&gt;

&lt;h2 id=&quot;1-learn-one-basic-coding-skill&quot;&gt;1. Learn one basic coding skill&lt;/h2&gt;

&lt;p&gt;Learn to do one thing and do it well.&lt;/p&gt;

&lt;p&gt;If you’re a frontend developer, practice turning a mockup into a webpage and calling existing API endpoints to make it work. Use React or Angular or whatever is the shiny thing these days.&lt;/p&gt;

&lt;p&gt;If you’re a backend developer, practice writing API endpoints that validate user data, put it into a database, and read it back.&lt;/p&gt;

&lt;p&gt;You’ll spend most of your time on those types of tasks.&lt;/p&gt;

&lt;h2 id=&quot;2-ask-for-help&quot;&gt;2. Ask for help&lt;/h2&gt;

&lt;p&gt;When you get an error message (it will happen a lot), don’t simply send it over to a coworker.&lt;/p&gt;

&lt;p&gt;Take the time to solve it on your own. Or at least, try. The first step? Google the same error message. Forget about AI for now. Only then, if you’re still stuck, ask a coworker, showing what you found and tried.&lt;/p&gt;

&lt;p&gt;You’ll get more help if you show you tried rather than simply saying “help me.”&lt;/p&gt;

&lt;h2 id=&quot;3-ask-questions&quot;&gt;3. Ask questions&lt;/h2&gt;

&lt;p&gt;You’ll have to ask lots and lots of questions. Like a lot.&lt;/p&gt;

&lt;p&gt;For me, everything was new at &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;my first job&lt;/a&gt;. The coding part, the business domain, working on a corporate job…&lt;/p&gt;

&lt;p&gt;Always ask why:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Why this task?&lt;/li&gt;
  &lt;li&gt;What’s the real problem?&lt;/li&gt;
  &lt;li&gt;And why solve it now?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s better if you annoy people with questions than with mistakes from not asking.&lt;/p&gt;

&lt;h2 id=&quot;4-find-good-role-models-and-mentors&quot;&gt;4. Find good role models and mentors&lt;/h2&gt;

&lt;p&gt;A good mentor will make a huge difference in your career.&lt;/p&gt;

&lt;p&gt;But don’t simply ask, “would you like to be my mentor?” Everybody will tell you no. Being a “mentor” implies commitment, most of the time, for free. &lt;a href=&quot;/2024/11/26/FindingMentors/&quot;&gt;Find alternative ways to be mentored&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Often, books are your best mentors too. &lt;em&gt;If only someone had written a book sharing his coding lessons…Wink, wink!&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;5-be-comfortable-with-the-struggle&quot;&gt;5. Be comfortable with the struggle&lt;/h2&gt;

&lt;p&gt;At the beginning, it sucks.&lt;/p&gt;

&lt;p&gt;You feel like you don’t know anything. Everybody around you is way more competent. You go to bed while leaving something broken to wait for you the next day.&lt;/p&gt;

&lt;p&gt;Take it step by step. It gets easier with time and effort. And the feeling of not knowing anything never goes away. You simply change the things you don’t know now with others.&lt;/p&gt;

&lt;p&gt;Trust the process. The struggle is part of it.&lt;/p&gt;

&lt;h2 id=&quot;6-learn-the-right-things-at-the-right-time&quot;&gt;6. Learn the right things at the right time&lt;/h2&gt;

&lt;p&gt;At the beginning, I tried to learn about everything at once.&lt;/p&gt;

&lt;p&gt;I was into Python, &lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;learning C#&lt;/a&gt;, reading &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt;, and keeping up with PHP…Lots of passion, without direction.&lt;/p&gt;

&lt;p&gt;Coding isn’t simply one skill, but lots of subskills:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Language syntax&lt;/li&gt;
  &lt;li&gt;Problem solving&lt;/li&gt;
  &lt;li&gt;Databases&lt;/li&gt;
  &lt;li&gt;Development practices…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tackle them one by one. Only move to the next skill once you understand one enough.&lt;/p&gt;

&lt;p&gt;Remember you don’t need to know everything. And you don’t need to know everything at once either.&lt;/p&gt;

&lt;h2 id=&quot;7-learn-to-look-for-your-own-answers&quot;&gt;7. Learn to look for your own answers&lt;/h2&gt;

&lt;p&gt;University, bootcamps, and YouTube won’t teach you everything you need to know.&lt;/p&gt;

&lt;p&gt;You have to read and study a lot. Like a lot! Nobody taught me about C#, &lt;a href=&quot;/2020/05/29/HowToVersionControl/&quot;&gt;version control&lt;/a&gt;, or &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;unit testing&lt;/a&gt;…and on and on. I had to study them on my own.&lt;/p&gt;

&lt;p&gt;Being a good coder also means being a lifelong learner.&lt;/p&gt;

&lt;h2 id=&quot;8-be-willing-to-learn-and-follow-instructions&quot;&gt;8. Be willing to learn and follow instructions&lt;/h2&gt;

&lt;p&gt;As a new coder, you stand out by showing that you’re able to learn and follow instructions.&lt;/p&gt;

&lt;p&gt;For example, when you ask for help (See #2), do or at least try doing what you’re being told.&lt;/p&gt;

&lt;h2 id=&quot;9-introduce-yourself-network-and-write-cvs&quot;&gt;9. Introduce yourself, network, and write CVs&lt;/h2&gt;

&lt;p&gt;It’s hard to land a job when you have almost 0 hours of flight time.&lt;/p&gt;

&lt;p&gt;You don’t know many people. You don’t have much to &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;show off on your CV&lt;/a&gt;. You have to resort to networking. &lt;a href=&quot;/2025/02/03/LandingACodingJob/&quot;&gt;Find clever ways to get an introduction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Back in my day, I got my first job because I knew someone who knew someone. These days, I’d try starting with social media. &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;Go with LinkedIn&lt;/a&gt; if you don’t know where to start.&lt;/p&gt;

&lt;p&gt;And when you get an introduction, say you’re starting out and you’re willing to learn. See #8.&lt;/p&gt;

&lt;h2 id=&quot;10-real-coding-isnt-like-university-or-bootcamps&quot;&gt;10. Real coding isn’t like University or bootcamps&lt;/h2&gt;

&lt;p&gt;In real life, often there’s no documentation, standard procedures, development workflows, or software methodologies.&lt;/p&gt;

&lt;p&gt;Or it’s more like a zombie methodology disguised as Agile. And on top of that, the existing code is a mess. Welcome to coding in the real world!&lt;/p&gt;

&lt;h2 id=&quot;bonus-my-last-piece-of-advice&quot;&gt;(Bonus) My last piece of advice&lt;/h2&gt;

&lt;p&gt;Once you’re in a job, forget about the job description you read before joining. Learn and absorb everything you can, don’t be afraid to experiment. Embrace every opportunity to grow and challenge yourself.&lt;/p&gt;

&lt;p&gt;When your current role becomes second nature, it’s your signal to move one for new challenges. Never stop learning!&lt;/p&gt;

&lt;p&gt;If you liked these 10 tips, I cover similar tips (and others) in more depth in my book, &lt;strong&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=tips-every-new-coder-know-succeed&quot;&gt;Street-Smart Coding&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;—It’s the roadmap I wish I had on my journey from junior to senior.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Change Starts in Your Mind, not Anywhere Else</title>
   <link href="https://canro91.github.io/2025/03/07/ChangeStartsInYourMind/"/>
   <updated>2025-03-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/07/ChangeStartsInYourMind</id>
   <content type="html">&lt;p&gt;Hollywood often glorifies Navy SEALs portraying them as indestructible and focused men.&lt;/p&gt;

&lt;p&gt;Jocko Willink, the guy from American Sniper, and the guys from Lone Survivor are all portrayed of them self-disciplined tough men.&lt;/p&gt;

&lt;p&gt;But that’s not always the case. And this isn’t a story of a self-disciplined man.&lt;/p&gt;

&lt;p&gt;His name is Taylor Cavanaugh, an ex-SEAL.&lt;/p&gt;

&lt;p&gt;After joining the Military, Taylor’s life was a disaster. Bad decision after bad decision kicked him out of the military and led him to escape to France to join the Foreign Legion.&lt;/p&gt;

&lt;p&gt;He tells his full story here:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/NbzZS1Aqxew?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;And here are my 3 favorite quotes from his story.&lt;/p&gt;

&lt;h2 id=&quot;if-you-feel-like-its-the-happiest-youve-ever-been-be-very-careful&quot;&gt;“If you feel like it’s the happiest you’ve ever been, be very careful”&lt;/h2&gt;

&lt;p&gt;Taylor’s breaking point was after graduating from his military training.&lt;/p&gt;

&lt;p&gt;It was partying and living “la vida loca” that got Taylor into trouble. It was the feeling of making it.&lt;/p&gt;

&lt;p&gt;My breaking point wasn’t when I was making minimum wage at &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;my first job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No! It was years later when I was making the most money I had made as a software engineer. It was when I had won a “reputation” and felt comfortable at work. That’s when I got into trouble. I burned out.&lt;/p&gt;

&lt;p&gt;And if we aren’t careful enough in those happy moments, everything that goes up has to go down.&lt;/p&gt;

&lt;h2 id=&quot;you-cannot-get-healthy-in-the-same-place-that-makes-you-sick&quot;&gt;“You cannot get healthy in the same place that makes you sick”&lt;/h2&gt;

&lt;p&gt;Often we can’t afford moving to a new place.&lt;/p&gt;

&lt;p&gt;That’s what Taylor did to change his life. He moved to France to join the Foreign Legion. A military unit that accepts foreigners and give them a new name and life, no questions asked.&lt;/p&gt;

&lt;p&gt;But we can change our environment without leaving home by:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;cleaning our room,&lt;/li&gt;
  &lt;li&gt;working out,&lt;/li&gt;
  &lt;li&gt;making our bed,&lt;/li&gt;
  &lt;li&gt;eating healthy food,&lt;/li&gt;
  &lt;li&gt;ditching social media, and&lt;/li&gt;
  &lt;li&gt;going to bed earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It took me months to start &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;getting out of that burnout episode&lt;/a&gt;. But, it all started with a 5-kg dumbbell and a YouTube video to work out before showing up to work and with the type of content I was consuming online.&lt;/p&gt;

&lt;p&gt;Move to another place or create the right conditions around you to flourish.&lt;/p&gt;

&lt;h2 id=&quot;anything-you-do-starts-with-self-development&quot;&gt;“Anything you do starts with self development”&lt;/h2&gt;

&lt;p&gt;Nobody is coming to save you.&lt;/p&gt;

&lt;p&gt;It was a little voice in my head when I hit rock bottom that helped me to get up. “If you don’t get up by yourself, nobody is going to do it for you.”&lt;/p&gt;

&lt;p&gt;I had to believe it was possible. I started to read inspiring books and YouTube videos. I had to watch stories from other who overcame similar situations in their lives. All that to believe I could do it too.&lt;/p&gt;

&lt;p&gt;Change has to start in your mind first.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting thought&lt;/h2&gt;

&lt;p&gt;After overcoming his messy life, now Taylor is a life coach.&lt;/p&gt;

&lt;p&gt;Me? Well, I took almost a year off to continue taking care of my health. I adopted a simple habit: &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;ditching my to-do list&lt;/a&gt; and doing something for my body, mind, and spirit every day. Every single day, without excuses.&lt;/p&gt;

&lt;p&gt;And that last quote summarizes Taylor’s journey through recovery—and my journey too. It all starts in your mind with your beliefs. Because beliefs turn into actions, actions into habits, and habits into change.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Better Organize Your Program.cs File in ASP.NET Core Apps</title>
   <link href="https://canro91.github.io/2025/03/06/OrganizeProgramDotCs/"/>
   <updated>2025-03-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/06/OrganizeProgramDotCs</id>
   <content type="html">&lt;p&gt;If you’re not careful, your Program.cs file can become a mess.&lt;/p&gt;

&lt;p&gt;It can turn into a long class full of methods and conditionals for every dependency to configure. We focus on the rest of our code, but often forget about the Program.cs file.&lt;/p&gt;

&lt;p&gt;We could try extension methods to keep our configurations clean and organized.&lt;/p&gt;

&lt;p&gt;But, these days, while working with a client, I learned an alternative to extension methods for keeping our Program.cs file tidy. A coworker showed me this approach. He learned it from a past job.&lt;/p&gt;

&lt;p&gt;Here’s how to do it:&lt;/p&gt;

&lt;h2 id=&quot;1-lets-create-an-aspnet-core-project-adding-hangfire&quot;&gt;1. Let’s create an ASP.NET Core project adding Hangfire&lt;/h2&gt;

&lt;p&gt;Let’s create a dummy ASP.NET Core app. And to make it a bit more “complicated,” let’s add &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;a lite Hangfire&lt;/a&gt; with one recurring job.&lt;/p&gt;

&lt;p&gt;Here’s our unorganized Program.cs file,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire.Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseInMemoryStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHangfireServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SchedulePollingInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkerCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;GlobalJobFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutomaticRetryAttribute&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Attempts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseHangfireDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapHangfireDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;0/1 * * * *&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TriggerJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nothing fancy. A bunch of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddSomething()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseSomething()&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;It’s already kind of a mess, right? Looks familiar?&lt;/p&gt;

&lt;h2 id=&quot;2-lets-register-each-dependency-using-a-separate-class&quot;&gt;2. Let’s register each dependency using a separate class&lt;/h2&gt;

&lt;p&gt;To make our app work, we must register controllers and Hangfire. Let’s do it in a new class called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyCoolAppUsingHangfire&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire.Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OrganizingProgramDotCs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyCoolAppUsingHangfire&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseWebApp&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                                    ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                      ^^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ControllersConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HangfireConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// One class to register controllers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ControllersConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfigureApp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Another class to register Hangfire&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HangfireConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfigureApp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseHangfireDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapHangfireDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;0/1 * * * *&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TriggerJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseInMemoryStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHangfireServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SchedulePollingInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkerCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;GlobalJobFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutomaticRetryAttribute&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Attempts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyCoolAppUsingHangfire&lt;/code&gt; has only one method: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegisterConfiguration()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside it, we register two classes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ControllersConfig&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HangfireConfig&lt;/code&gt;. One “config” class per “artifact” to register.&lt;/p&gt;

&lt;p&gt;Each config class implements &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfigureServices&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IConfigureApp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside each config class, we put what was scattered all over the Program.cs file.&lt;/p&gt;

&lt;h2 id=&quot;3-lets-look-at-basewebapp&quot;&gt;3. Let’s look at BaseWebApp&lt;/h2&gt;

&lt;p&gt;Inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BaseWebApp&lt;/code&gt;, the real magic happens,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OrganizingProgramDotCs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BaseWebApp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfigure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_configurations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegisterConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                      ^^^^^&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfigure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunAppAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;RegisterConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_configurations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfigureApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IConfigure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IConfigureApp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfigure&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;webApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IConfigureServices&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfigure&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RunAppAsync()&lt;/code&gt; looks almost like the content of a normal Program.cs.&lt;/p&gt;

&lt;p&gt;But, it reads the services and configurations to register from a list, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_configurations&lt;/code&gt;. We populate that list inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyCoolAppUsingHangfire&lt;/code&gt; using the method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Register()&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;after-that-change-our-programcs-file-has-only-a-few-lines-of-code&quot;&gt;After that change, our Program.cs file has only a few lines of code&lt;/h2&gt;

&lt;p&gt;And lo and behold,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OrganizingProgramDotCs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myCoolApp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyCoolAppUsingHangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myCoolApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RunAppAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this approach, we move every configuration artifact to separate classes, keeping the Program.cs clean and compact. Ours now has only three lines of code.&lt;/p&gt;

&lt;p&gt;We could also use this approach &lt;a href=&quot;/2024/09/25/MigratingStartupClass/&quot;&gt;to handle the Startup class when migrating old ASP.NET Core projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Bring Out Your Writing Voice With This Simple Technique</title>
   <link href="https://canro91.github.io/2025/03/05/WritingVoice/"/>
   <updated>2025-03-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/05/WritingVoice</id>
   <content type="html">&lt;p&gt;The biggest mistake all new writers make is trying to sound like a “writer.”&lt;/p&gt;

&lt;p&gt;That’s because we’re more used to reading fiction. And we try to do the same. But we end up writing long sentences and using complicated words.&lt;/p&gt;

&lt;p&gt;Instead of pretending to be a writer, &lt;a href=&quot;/2024/12/24/WritingVoice/&quot;&gt;write for a single person in mind&lt;/a&gt;. That’s what I do.&lt;/p&gt;

&lt;p&gt;But these days, I’ve found another technique to bring out your inner and real voice out when writing.&lt;/p&gt;

&lt;p&gt;It comes from Henrik Karlsson from Escaping Flatland. He wrote in &lt;a href=&quot;https://www.henrikkarlsson.xyz/p/start-a-blog&quot;&gt;Advice for a friend who wants to start a blog&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;#4. People tend to sound more like themselves in chat messages than in blog posts. So perhaps write in the chat, rapidly, to a friend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we’re texting friends or family, we don’t use jargon, fluff, or long sentences. We use the tone we use in a face-to-face conversation. A text box inside WhatsApp brings your inner voice out.&lt;/p&gt;

&lt;p&gt;The next time you sit down to write, open a chat with yourself and let your true voice flow.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Career Lessons I Wish Someone Had Told Me Before</title>
   <link href="https://canro91.github.io/2025/03/04/CareerLessonIWishIKnew/"/>
   <updated>2025-03-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/04/CareerLessonIWishIKnew</id>
   <content type="html">&lt;p&gt;Over 10 years ago, I was fired from my first job.&lt;/p&gt;

&lt;p&gt;I worked as an entry-level software engineer. I learned a lot from that job. I had to learn about coding and to make my way through the corporate world while growing my career.&lt;/p&gt;

&lt;p&gt;Coding was the easy part. I had books, courses, and tutorials for that. But to survive the corporate world? Not so much. I had to figure it out on my own, with lots of trial and error, and &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;yes, getting fired&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are 10 career lessons I wish someone had told me over 10 years ago:&lt;/p&gt;

&lt;h2 id=&quot;1-be-ready-to-leave-your-job-at-any-time&quot;&gt;1. Be ready to leave your job at any time&lt;/h2&gt;

&lt;p&gt;A 9-5 is never safe.&lt;/p&gt;

&lt;p&gt;You could lose your job at any time for reasons you don’t control. Yesterday? A pandemic, global crisis, recession, and high interest rates. Today? DOGE and AI. Tomorrow? Who knows.&lt;/p&gt;

&lt;p&gt;Be ready to leave:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Grow your professional network&lt;/li&gt;
  &lt;li&gt;Keep your “tell me about yourself muscles” in shape&lt;/li&gt;
  &lt;li&gt;Have an emergency fund to cover a few months of expenses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you’re let go, you’ll have options, not just panic.&lt;/p&gt;

&lt;h2 id=&quot;2-an-online-presence-is-your-best-cv-and-portfolio&quot;&gt;2. An online presence is your best CV and portfolio&lt;/h2&gt;

&lt;p&gt;CVs are so last century.&lt;/p&gt;

&lt;p&gt;I wasn’t hired directly for my first job. I was hired under a staffing company. My salary passed from another couple of hands before getting into my bank account. Of course, they took a good chunk of it.&lt;/p&gt;

&lt;p&gt;One day, I had to visit the office of the staffing company for some paperwork. Next to the main door, there was a sign: “Put your CV here.” It was a trash bin.&lt;/p&gt;

&lt;p&gt;Well, the same type of receptacle offices used as trash bins. I guess it saved them the struggle of piling up and throwing away all the CVs from desperate people looking for a minimum wage job. They were already on a trash bin. That day, I knew the system was broken.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;Ditch your CV&lt;/a&gt; and &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;appear professionally anywhere online&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-pay-raises-wont-change-an-unfulfilling-job&quot;&gt;3. Pay raises won’t change an unfulfilling job&lt;/h2&gt;

&lt;p&gt;Vacations and pay raises won’t help you when you don’t want to get out of bed.&lt;/p&gt;

&lt;p&gt;If Monday mornings are torture, or even worse, if Sunday evenings are also torture, you don’t need vacations. You need a way out of your job.&lt;/p&gt;

&lt;p&gt;Trust me, I got burned out. And not wanting to get out of bed was the first sign I failed to notice. I learned it the hard way.&lt;/p&gt;

&lt;p&gt;Find something that excites you to get up every morning.&lt;/p&gt;

&lt;h2 id=&quot;4-hoping-and-praying-is-a-bad-career-strategy&quot;&gt;4. Hoping and praying is a bad career strategy&lt;/h2&gt;

&lt;p&gt;Make a career plan and always have an exit route for every job.&lt;/p&gt;

&lt;p&gt;OK, a career plan sounds like a lot of commitment, especially when we’re just starting. Let’s say, set an intention for your career. Do you want money, connections, recognition, travel opportunities, etc.?&lt;/p&gt;

&lt;p&gt;Otherwise, society will give you its plan: work hard, get 3% yearly raises, please your bosses, keep your head down, then wait to retire.&lt;/p&gt;

&lt;h2 id=&quot;5-soft-skills-are-more-important-than-hard-skills&quot;&gt;5. Soft skills are more important than hard skills&lt;/h2&gt;

&lt;p&gt;Being good at coding, writing, or designing will give you a job, but being good at communication will give you promotions.&lt;/p&gt;

&lt;p&gt;Your success depends on how good you are at communicating, managing, and coordinating people. I’m paraphrasing a lesson I learned from How to Win Friends and Influence People.&lt;/p&gt;

&lt;p&gt;Work on your soft skills.&lt;/p&gt;

&lt;h2 id=&quot;6-detach-your-sense-of-meaning-from-your-work&quot;&gt;6. Detach your sense of meaning from your work&lt;/h2&gt;

&lt;p&gt;Being a “Senior Software Engineer” was my only identity.&lt;/p&gt;

&lt;p&gt;My career was probably the most important part of my life. It was what gave me money and a sense of meaning.&lt;/p&gt;

&lt;p&gt;Until &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;I was laid off&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Suddenly, after one Zoom meeting, the title and the things I did with it were gone. It shook my world. That was the only thing I thought I enjoyed.&lt;/p&gt;

&lt;p&gt;I had to learn I’m not a title. I’m more than a job. “Senior Software Engineer” was just a label I decided to accept. I was more than that.&lt;/p&gt;

&lt;p&gt;You’re more than a job title.&lt;/p&gt;

&lt;h2 id=&quot;7-dont-be-the-only-one-doing-something-at-work&quot;&gt;7. Don’t be the only one doing something at work&lt;/h2&gt;

&lt;p&gt;It feels so good when you’re the only one who knows or does something.&lt;/p&gt;

&lt;p&gt;But that’s a trap. Being the “only one” makes you a hero. And a hero can’t get sick, go on vacation, or be promoted. A hero is always stuck.&lt;/p&gt;

&lt;p&gt;Don’t turn yourself into a hero. Teach, automate, or document what you only know or do.&lt;/p&gt;

&lt;p&gt;Don’t be a hero. Be a team player.&lt;/p&gt;

&lt;h2 id=&quot;8-find-mentors-but-dont-ask-anyone-to-be-your-mentor&quot;&gt;8. Find mentors but don’t ask anyone to be your mentor&lt;/h2&gt;

&lt;p&gt;I put “Senior” in my title after one or two years of working next to the right people at one of my first jobs.&lt;/p&gt;

&lt;p&gt;They taught me in just one year way more than what I learned in the next 5 years after I left that job.&lt;/p&gt;

&lt;p&gt;A good mentor or role model can help you advance in your career faster.&lt;/p&gt;

&lt;p&gt;But &lt;a href=&quot;/2024/11/26/FindingMentors/&quot;&gt;don’t ask anyone to be your mentor&lt;/a&gt;. Don’t drop the M-word. “Would you be my mentor?” That’s a strong ask. It implies commitment from one side more than the other. Often for free.&lt;/p&gt;

&lt;p&gt;Find ways to learn from your role models without the M-word. Find not-mentors.&lt;/p&gt;

&lt;h2 id=&quot;9-two-years-is-the-tipping-point-for-growth-at-a-company&quot;&gt;9. Two years is the tipping point for growth at a company&lt;/h2&gt;

&lt;p&gt;I’ve stayed too long at stagnant jobs and I regretted it.&lt;/p&gt;

&lt;p&gt;I lost years, thousands of dollars an let some of my skills get rusty.&lt;/p&gt;

&lt;p&gt;At one place, I stayed too long waiting for my chance. I tried to convince upper management I deserved a promotion. All the seats were taken. Every one of my ideas for roles was rejected. “Somebody is kind of already doing that.”&lt;/p&gt;

&lt;p&gt;If after two or three years of doing good work, you don’t get a raise or a promotion, don’t wait any longer. You’re at the wrong place. Move on.&lt;/p&gt;

&lt;p&gt;You’re leaving money on the table by staying too long at the same place.&lt;/p&gt;

&lt;h2 id=&quot;10-you-will-be-remembered-by-your-attitude-not-by-your-work&quot;&gt;10. You will be remembered by your attitude, not by your work&lt;/h2&gt;

&lt;p&gt;The long hours, the impeccable report, the great presentation. Nobody will remember them.&lt;/p&gt;

&lt;p&gt;They will remember your answers in meetings, your willingness to help others, your treatment of clients.&lt;/p&gt;

&lt;p&gt;Being easy to work with will pay off in your career. That’s the easiest way to &lt;a href=&quot;/2025/02/26/StandOutAtWork/&quot;&gt;stand out at work&lt;/a&gt; and to leave a lasting impression.&lt;/p&gt;

&lt;p&gt;Remember, as Robert Martin said in the &lt;a href=&quot;/2020/06/15/CleanCoder/&quot;&gt;Clean Coder&lt;/a&gt;, &lt;em&gt;“your career is your responsibility, not your employer’s.”&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>To Work From Home, You Need Clear Boundaries Between Work and Non-Work</title>
   <link href="https://canro91.github.io/2025/03/03/Boundaries/"/>
   <updated>2025-03-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/03/Boundaries</id>
   <content type="html">&lt;p&gt;He said, “You have put boundaries around your time.”&lt;/p&gt;

&lt;p&gt;I was on a 1-on-1 with my &lt;a href=&quot;/2024/11/26/FindingMentors/&quot;&gt;boss’s boss and not-mentor&lt;/a&gt; at a past job. He was right. I had boundaries around my working hours. I always clocked out on time, and sometimes even earlier.&lt;/p&gt;

&lt;p&gt;On more than one occasion, my team leader texted me or called me at 4:50 PM sharing details of a task or a bug. “Sorry, but I can’t finish that today. It’s 10 minutes to my end of day and that will take me at least a couple of hours,” I always said.&lt;/p&gt;

&lt;p&gt;Eventually he stopped calling me 10 minutes before my end of day.&lt;/p&gt;

&lt;h2 id=&quot;the-hardest-part-of-working-from-home&quot;&gt;The hardest part of working from home&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2020/08/08/LessonsOnRemoteWork/&quot;&gt;I’ve worked remotely for over 5 years&lt;/a&gt;. The hardest part? Setting boundaries between working and non-working hours.&lt;/p&gt;

&lt;p&gt;When you’re at an office, some boundaries are clear. It’s lunch time. Or it’s 5:00 PM and you see a line of people clocking out. The day is over. No more meetings. No more calls.&lt;/p&gt;

&lt;p&gt;But from home, the line between work and non-work is blurry.&lt;/p&gt;

&lt;p&gt;From home, we’re a couple of steps away from “work” and we’re wearing pajamas or no pants like any other time of the day.&lt;/p&gt;

&lt;p&gt;And, if we don’t pay attention, we’re replying to emails and taking calls after hours. Or even working on weekends. Or thinking about work all day long.&lt;/p&gt;

&lt;h2 id=&quot;how-to-put-boundaries-between-work-and-non-work&quot;&gt;How to put boundaries between work and non-work&lt;/h2&gt;

&lt;p&gt;To start setting back those boundaries between work and non-work:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Have separate work and personal spaces&lt;/strong&gt;: Your work laptop is only for work stuff.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Turn off all notifications after working hours&lt;/strong&gt;: No Slack or Teams or email beeps or buzzes after 5:00 PM.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do something that signals the end of your working hours&lt;/strong&gt;: Walk your dog, change clothes, or go to a different room.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Uninstall work-related messaging apps from your phone&lt;/strong&gt;: No Slack or Teams or work email on the phone.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re working from a different time zone than your coworkers, you don’t have to reply after hours, not even to say it’s already past your working hours. Reply the next work day.&lt;/p&gt;

&lt;p&gt;And if you’re the one texting, start your messages with a disclaimer, something like “When you’re back online tomorrow: blah, blah, blah” or schedule your messages. And, please &lt;a href=&quot;/2024/11/27/BeingIgnoredAtWork/&quot;&gt;don’t send “hello, how are you” messages&lt;/a&gt;. That’s how you get ignored at work.&lt;/p&gt;

&lt;p&gt;Remember, working from home doesn’t mean being available 24/7.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>One Simple Trick That Will Make Your Virtual Meetings Smoother</title>
   <link href="https://canro91.github.io/2025/03/02/SmootherMeetings/"/>
   <updated>2025-03-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/02/SmootherMeetings</id>
   <content type="html">&lt;p&gt;If you work remotely, this scene will feel familiar during virtual meetings:&lt;/p&gt;

&lt;p&gt;Someone is talking, makes a little pause. Somebody else interrupts, then the first person starts talking. A pause. Then, a “you go first,” followed by a “no, no, no. You go first.” Then, both of them talk at the same time. Arrggg!&lt;/p&gt;

&lt;p&gt;Here’s a trick to avoid that annoying situation:&lt;/p&gt;

&lt;p&gt;Just like pilots or anyone using “single-channel” radio, pause for a few seconds after someone finishes speaking before you respond.&lt;/p&gt;

&lt;p&gt;With this brief pause, you give time to the speaker to finish their chain of thoughts and wait long enough for the latency of getting the message on your side.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Replaced Grammarly with This AI Prompt (Tested on Copilot)</title>
   <link href="https://canro91.github.io/2025/03/01/ReplacingGrammarly/"/>
   <updated>2025-03-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/03/01/ReplacingGrammarly</id>
   <content type="html">&lt;p&gt;AI is here to stay.&lt;/p&gt;

&lt;p&gt;We can’t ignore it. We can’t refuse to use it. &lt;a href=&quot;/2025/01/21/AIAdaptation/&quot;&gt;We have to adapt&lt;/a&gt;, or somebody with AI will replace us.&lt;/p&gt;

&lt;p&gt;I’m not in the “Never use AI” team. In fact, I already ran the experiment of &lt;a href=&quot;/2024/03/18/AIToLaunchMyCourses/&quot;&gt;launching a coding course with Copilot as my assistant&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what I don’t agree with is using AI to replace my thinking and writing.&lt;/p&gt;

&lt;p&gt;That’s why every single post you find here (or anywhere else online under my name) is written by me—a human, not an AI. #madebyahuman&lt;/p&gt;

&lt;p&gt;Even though, I don’t use AI to regurgitate hallucinations, &lt;a href=&quot;/2025/02/24/AVeteranOnAI/&quot;&gt;a veteran coder warned me about that the other day&lt;/a&gt;, I use AI to proofread and edit my posts. Here’s the prompt I use:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You’re an expert on writing and editing, show the grammar and spelling issues from the next text. Fix those issues, keeping the original tone. Bold all the words you change. Only the words. This is the text:&lt;/p&gt;

  &lt;p&gt;{add your text here}&lt;/p&gt;

  &lt;p&gt;Don’t rewrite it. Only fix the grammar and typos. Remember to keep the same tone and structure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I tested it on Copilot. It should work on ChatGPT too. Give it a try and see if you can replace Grammarly with that prompt as well.&lt;/p&gt;

&lt;p&gt;PS: I proofread and edited this post with the very same prompt. No robots were harmed in the making of this post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What Frustrates Me the Most as a C#/.NET Developer</title>
   <link href="https://canro91.github.io/2025/02/28/Frustrations/"/>
   <updated>2025-02-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/28/Frustrations</id>
   <content type="html">&lt;p&gt;C# has put a roof over my head and food on my table for more than 10 years.&lt;/p&gt;

&lt;p&gt;At university, I learned Java. It was a relief coming from C/C++. Java didn’t have all the things I hated about C. I’m looking at you, pointers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;At my first job&lt;/a&gt;, I had to learn C#. The first program I wrote there was a Java program with C# keywords. Oops! Java was the only language I knew at that time.&lt;/p&gt;

&lt;p&gt;I like C# and the entire .NET ecosystem. A typed language, multi-paradigm, with good tooling and stable support.&lt;/p&gt;

&lt;p&gt;But here are the things that frustrate me the most about .NET:&lt;/p&gt;

&lt;h2 id=&quot;1-naming&quot;&gt;1. Naming&lt;/h2&gt;

&lt;p&gt;Naming is one of the two hardest parts of Computer Science. And Microsoft doesn’t help that much.&lt;/p&gt;

&lt;p&gt;On one hand, we have “.NET Core” renamed to “.NET”. Everything is .NET now. Was it a marketing strategy? Dunno. Probably.&lt;/p&gt;

&lt;p&gt;On the other hand, target framework monikers. You know, the version number we put inside our .csproj files. For some time, they were &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.netcoreapp1.X&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.netcoreapp2.X&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.netcoreapp3.X&lt;/code&gt;. But one day, they changed it.&lt;/p&gt;

&lt;p&gt;I imagine a conversation somewhere on Teams at Microsoft like this:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Let’s change monikers too. Let’s also use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.net&lt;/code&gt; plus the version number.&lt;/li&gt;
  &lt;li&gt;Wait, we can’t do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.net4&lt;/code&gt;. We already have a .NET Framework 4.0. People will get confused.&lt;/li&gt;
  &lt;li&gt;Ok, let’s jump to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.net5&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Arrggg! Microsoft and names.&lt;/p&gt;

&lt;h2 id=&quot;2-too-many-releases&quot;&gt;2. Too many releases&lt;/h2&gt;

&lt;p&gt;It’s a good thing we have an evolving ecosystem.&lt;/p&gt;

&lt;p&gt;I used to read all the release notes and tried to pick up as many new features as I could. Now? I only care about long-term versions. I don’t even pay attention to the short-term ones. Something somewhere is a bit faster on an architecture I don’t use at work. Sorry Microsoft!&lt;/p&gt;

&lt;p&gt;Too many releases make it harder to keep up.&lt;/p&gt;

&lt;h2 id=&quot;3-c-is-getting-too-bloated&quot;&gt;3. C# is getting too bloated&lt;/h2&gt;

&lt;p&gt;C# doesn’t feel like a single language anymore.&lt;/p&gt;

&lt;p&gt;It feels like three languages: one pre-2010, one around 2010, and the one we have now.&lt;/p&gt;

&lt;p&gt;I used to closely follow every new language release. Not anymore. &lt;a href=&quot;/2024/07/08/CSharpInconsistencies/&quot;&gt;C# as a language is getting less consistent&lt;/a&gt;. Too many options to create and initialize objects, for example.&lt;/p&gt;

&lt;p&gt;Apart from &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;nullable references&lt;/a&gt; and pattern matching and maybe some others I can’t remember now, it’s more and more syntactic sugar on every release. I’m only waiting for &lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;discriminated unions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The worst part is features that look the same but work differently. Yes, I’m looking at you, primary constructors. They look like records, but surprise, surprise…They work differently.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ThisIsARecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThisIsAPublicProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ThisIsAClassUsingPrimaryConstructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThisIsNotAPublicProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This inconsistency makes the language harder to teach and learn.&lt;/p&gt;

&lt;h2 id=&quot;4-automapper&quot;&gt;4. AutoMapper&lt;/h2&gt;

&lt;p&gt;Ok, there’s nothing wrong with AutoMapper.&lt;/p&gt;

&lt;p&gt;But what frustrates me is that, for some reason, we have adopted it as the de facto mapping library. And most of the time, AutoMapper ends up getting in our way.&lt;/p&gt;

&lt;p&gt;Even AutoMapper’s author recommends not to use it if we’re mapping more than 80% of our fields by hand. But anyway, we use it even when we shouldn’t.&lt;/p&gt;

&lt;p&gt;Just in the past weeks, I found two scenarios that got in my way, &lt;a href=&quot;/2025/01/24/IgnoringPropertiesAutoMapper/&quot;&gt;ignoring unmapped fields in the destination type&lt;/a&gt; and &lt;a href=&quot;/2025/02/13/AutoMapperValidations/&quot;&gt;getting mappings flagged as invalid&lt;/a&gt;. Sure, I know I was abusing AutoMapper.&lt;/p&gt;

&lt;p&gt;I wanted to add EntityFramework Core to this list, but I’m starting to feel the frustration in my stomach. Probably, I’m hungry. But, frustrations aside, .NET is still my go-to platform and C#, my go-to language.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Most Important Skill To Learn In The Next 10 Years</title>
   <link href="https://canro91.github.io/2025/02/27/TheMostImportantSkill/"/>
   <updated>2025-02-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/27/TheMostImportantSkill</id>
   <content type="html">&lt;p&gt;It isn’t writing, marketing, or social media.&lt;/p&gt;

&lt;p&gt;With the possible &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;threat of AI&lt;/a&gt;, there’s something else we must learn to future-proof ourselves.&lt;/p&gt;

&lt;p&gt;This time, Devon Eriksen sat down to talk to Dan Koe. Devon is a software engineer turned author. But he considers himself a libertarian. Devon wrote Theft of Fire, a sci-fi book.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/18ahFtnuBwI?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Here are 10 lessons I learned from watching that conversation:&lt;/p&gt;

&lt;h2 id=&quot;1-only-slaves-did-one-thing&quot;&gt;1. Only slaves did one thing&lt;/h2&gt;

&lt;p&gt;In ancient Rome, only slaves were meant to do a single job for life.&lt;/p&gt;

&lt;p&gt;Free-thinking men were supposed to learn on their own and do multiple things. We’re free-thinking men. We don’t have to do the same thing for life. Gone are the days when we have the same job for 40 or 50 years.&lt;/p&gt;

&lt;h2 id=&quot;2-the-most-important-skill-to-learn-is-agency&quot;&gt;2. The most important skill to learn is agency&lt;/h2&gt;

&lt;p&gt;Agency is “the tendency to initiate action to achieve your goals.” Agency makes you willing to take risks and become resilient to failure.&lt;/p&gt;

&lt;h2 id=&quot;3-agency-is-a-skill-we-can-learn&quot;&gt;3. Agency is a skill we can learn&lt;/h2&gt;

&lt;p&gt;To learn agency:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Start something new,&lt;/li&gt;
  &lt;li&gt;Learn as you go, and&lt;/li&gt;
  &lt;li&gt;Give yourself permission to suck.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4-the-biggest-risk-to-your-agency-is-your-limiting-beliefs&quot;&gt;4. The biggest risk to your agency is your limiting beliefs&lt;/h2&gt;

&lt;p&gt;We are our biggest haters with our self-talk: I can’t, I don’t know, and I don’t have.&lt;/p&gt;

&lt;h2 id=&quot;5-we-dont-need-permission-to-create&quot;&gt;5. We don’t need permission to create&lt;/h2&gt;

&lt;p&gt;We don’t need large TV networks, publishers, or media companies. The Internet has created a world without permission. We only need an internet connection and the desire to start.&lt;/p&gt;

&lt;h2 id=&quot;6-ask-what-is-the-purpose-behind-what-you-do&quot;&gt;6. Ask what is the purpose behind what you do&lt;/h2&gt;

&lt;p&gt;OK, purpose is a big word. Let’s say goal. Ask what you want to take out from what you do. What is your goal?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Be creative?&lt;/li&gt;
  &lt;li&gt;Make a living?&lt;/li&gt;
  &lt;li&gt;Connect with interesting people?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;7-dont-whine-create-something-interesting-instead&quot;&gt;7. Don’t whine. Create something interesting instead&lt;/h2&gt;

&lt;p&gt;Stop blaming the Algorithm. Stop waiting to be lucky. Stop waiting for someone to choose you from the crowd.&lt;/p&gt;

&lt;p&gt;Instead, show your work. Create something interesting to attract smart people because smart people have more money.&lt;/p&gt;

&lt;h2 id=&quot;8-money-is-a-sign-of-fcks-given&quot;&gt;8. Money is a sign of f*cks given&lt;/h2&gt;

&lt;p&gt;Money is a by-product of your personal development and your ideas. The more people care about you and your work, the more money you’ll make.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If you say ‘I don’t want a lot of money’ what you’re saying is ‘I don’t want a lot of people to give a f* about me’”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;9-dont-become-a-tool&quot;&gt;9. Don’t become a tool&lt;/h2&gt;

&lt;p&gt;You’re a tool when you only do one thing, like slaves in ancient Rome. Still remember #1? You’re also a tool when you attach your identity and self worth to that one thing.&lt;/p&gt;

&lt;p&gt;That’s why AI threatens many jobs: when a faster and better tool appears it replaces slower and worse tools. Don’t be a slow and worse tool. Well, don’t be a tool at all.&lt;/p&gt;

&lt;h2 id=&quot;10-building-an-audience-online-is-about-being-trust-worthy&quot;&gt;10. Building an audience online is about being trust-worthy&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If you’re on the internet and that’s how you make your money, your product is you. Your ability to be interesting. Your ability to be informative. Your ability to be entertaining. Your ability to make something where people spend some time paying attention to it and they walk away saying ‘I liked that’.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;This was a liberating conversation for me. For a long time, I struggled to find my one thing. Doing one thing was scary to me. One day at a past job, in a moment of clarity, I wondered “Am I supposed to do this until the day I die?” Arrggg! Thanks, but no thanks.&lt;/p&gt;

&lt;p&gt;But, like free-thinking men in ancient times, we’re free to learn. We’re free to choose our own goals. We’re free to do many things. We’re free to create.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Ways to Stand Out at Work—Other than Work Hard</title>
   <link href="https://canro91.github.io/2025/02/26/StandOutAtWork/"/>
   <updated>2025-02-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/26/StandOutAtWork</id>
   <content type="html">&lt;p&gt;It took me two years to go from being just another developer in the room to sitting in meetings with the president.&lt;/p&gt;

&lt;p&gt;It was at a past job. Sure, it was a small software company in my city. So small everyone knew who the president was, and he recognized everyone’s faces in the hallways. That was a point in my favor.&lt;/p&gt;

&lt;p&gt;In my first years at that place, I was always behind: delivering my tasks late, staying after hours to keep up, and fixing bugs in my code.&lt;/p&gt;

&lt;p&gt;And I wasn’t my team leader’s favorite team member.&lt;/p&gt;

&lt;p&gt;It took me hours of studying and practice and hard work to prove I was more than a “problematic” developer.&lt;/p&gt;

&lt;h2 id=&quot;but-you-dont-need-to-work-hard-here-are-10-ways-to-stand-out-at-work&quot;&gt;But you don’t need to work hard, here are 10 ways to stand out at work:&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Deliver your work on time. Not early, and definitely not late.&lt;/li&gt;
  &lt;li&gt;Document and automate your work. Don’t become a bottleneck. &lt;a href=&quot;/2025/02/18/BeingTooHelpful/&quot;&gt;Don’t become a hero&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Ask for the context behind your tasks. Always ask why. Why this? And why now?&lt;/li&gt;
  &lt;li&gt;Get interested in the business side of your work. Understand how your work is helping the company make money.&lt;/li&gt;
  &lt;li&gt;Speak up in meetings. Ask questions and give suggestions.&lt;/li&gt;
  &lt;li&gt;Volunteer for the work nobody else wants to do. Make sure you can do it, or figure out how.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;Give 10 ideas&lt;/a&gt; to improve the company, the product, or the work around you.&lt;/li&gt;
  &lt;li&gt;Be reachable during working hours, especially when &lt;a href=&quot;/2020/08/08/LessonsOnRemoteWork/&quot;&gt;working from home&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Be easy to work with. A.k.a don’t be an ass*.&lt;/li&gt;
  &lt;li&gt;Make your boss look good in front of their bosses.&lt;/li&gt;
  &lt;li&gt;Make sure every task you get is solved. Either because you solve it or follow up with the right team or person to solve it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, those were 11 ways. I know I said there would be 10.&lt;/p&gt;

&lt;p&gt;But if I had to pick only one from that list, I’d go with #9: Be easy to work with. Invest in your soft skills or human skills. That’s the easiest way to stand out. People will remember you, not by your work, but by your attitude.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>You&apos;re Not Writing Enough</title>
   <link href="https://canro91.github.io/2025/02/25/YouAreNotWritingEnough/"/>
   <updated>2025-02-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/25/YouAreNotWritingEnough</id>
   <content type="html">&lt;p&gt;That was my favorite lesson from the book &lt;a href=&quot;/2025/02/14/WritingForDevelopers/&quot;&gt;Writing for Developers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We could always write more. I could have written more. I abandoned my blog for months back in 2023. I burned out and instead of doubling down on what I enjoyed, I abandoned it. Arrggg!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/26/ContentIsEverywhere/&quot;&gt;Content ideas and posts are everywhere&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every bug fixed is a post. Every subject you’re learning is a post. Every time you struggle to figure something out is a post. Every job interview. Every performance review. Every promotion. Every book. Every mentorship session. They’re all posts.&lt;/p&gt;

&lt;p&gt;The same way we read biographies to look for success clues, we could leave our own success clues in every post. We could build our own &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;public time capsule&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From &lt;a href=&quot;https://blog.chasingbrains.co/p/just-write-stuff&quot;&gt;Just write&lt;/a&gt; by “Chasing Brains”:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Don’t over-engineer your site. Don’t even check the grammar. Just write 100 words and hit publish.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No matter if it’s a small bug fix or a career event, don’t create, but document.&lt;/p&gt;

&lt;p&gt;If you’re interested in writing, read &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;four lessons for a coder struggling to write&lt;/a&gt; and &lt;a href=&quot;/2024/12/06/AlwaysWriting/&quot;&gt;always write about what you do at work&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>If a Veteran Is Skeptical About AI, We Shouldn&apos;t Worry About Our Jobs</title>
   <link href="https://canro91.github.io/2025/02/24/AVeteranOnAI/"/>
   <updated>2025-02-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/24/AVeteranOnAI</id>
   <content type="html">&lt;p&gt;Pierre started coding back in the 60s and still maintains a 30-year-old codebase.&lt;/p&gt;

&lt;p&gt;Thanks to the magic of the Internet, and to &lt;a href=&quot;/2025/02/05/LessonsFromAViralPost/&quot;&gt;one of my posts going viral&lt;/a&gt;, I’ve exchanged a couple of emails with Pierre. I don’t know him in real life, just on the Internet.&lt;/p&gt;

&lt;p&gt;His story has captivated me from the beginning.&lt;/p&gt;

&lt;p&gt;I know only about a few industry veterans, and even fewer who are still actively coding.&lt;/p&gt;

&lt;p&gt;Another of those veterans is Leslie at one of my past jobs. He was a Distinguished Engineer, a title created just for him. He was in charge of reviewing every single one of our PRs. He didn’t miss a single comma.&lt;/p&gt;

&lt;h2 id=&quot;after-going-through-every-boom-and-hype-heres-a-veterans-take-on-ai&quot;&gt;After going through every boom and hype, here’s a veteran’s take on AI&lt;/h2&gt;

&lt;p&gt;Since the 60s, Pierre has gone through every boom and hype in this industry.&lt;/p&gt;

&lt;p&gt;He witnessed the Y2K panic, the birth of Java, the rise of Agile, and probably more that I don’t remember now.&lt;/p&gt;

&lt;p&gt;In one of our emails, I asked Pierre about his take on AI. Today, we only see headlines of &lt;a href=&quot;/2024/12/08/CEOVsJanitor/&quot;&gt;CEOs claiming AI generates most of their code&lt;/a&gt;, along with headlines saying coders are doomed.&lt;/p&gt;

&lt;p&gt;But, here’s what Pierre, with his wealth of experience, told me about AI (slightly edited for brevity):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I’m skeptical about AI. I just don’t see automatically generating code from typical imprecise specifications…Apparently we’ve run out of Internet on which to train AI. It is now feeding on its own hallucinations, like mad cows eating their own prion-riddled offal, or the inbred Hapsburg dynasty.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If Pierre, who has seen quite a lot, is skeptical, we shouldn’t be worried. I’m not.&lt;/p&gt;

&lt;p&gt;Pierre reminded me of that joke/meme that we’re still safe because AI needs unambiguous and well-written requirements. As long as clients who don’t know what they want exist, we’re still in business.&lt;/p&gt;

&lt;p&gt;I don’t think AI is taking our jobs anytime soon. Sure, &lt;a href=&quot;/2025/01/21/AIAdaptation/&quot;&gt;we have to adapt&lt;/a&gt;. AI is changing the landscape. For sure, &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;coding in 2034 will look different&lt;/a&gt;. We can’t ignore it.&lt;/p&gt;

&lt;p&gt;AI is like using calculators in math classes. They make us faster, sure, but they can’t do the thinking for us.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Knowing Is Teaching, but Also Reinventing the Wheel</title>
   <link href="https://canro91.github.io/2025/02/23/KnowingIsReinventing/"/>
   <updated>2025-02-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/23/KnowingIsReinventing</id>
   <content type="html">&lt;p&gt;“What I cannot create, I do not understand.”&lt;/p&gt;

&lt;p&gt;That’s one of the quotes on Richard Feynman’s blackboard at the time of his death. &lt;a href=&quot;https://digital.archives.caltech.edu/collections/Images/1.10-29/&quot;&gt;See the full picture here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We truly have learned something when we can teach it. Feynman also said that we truly understand a subject when we can teach a beginner’s level lecture on that subject.&lt;/p&gt;

&lt;p&gt;But that first quote reminded me of the famous mantra among programmers and coders: “don’t reinvent the wheel.”&lt;/p&gt;

&lt;p&gt;Back in my first coding classes at university, one of my teachers taught us to write our own “Connection” class and use it wherever we go. We were learning how to connect to databases, probably using PHP or something.&lt;/p&gt;

&lt;p&gt;Well, that wasn’t code reuse. That was reinventing the wheel and taking our reinvented wheel with us everywhere. We had ORMs in those days.&lt;/p&gt;

&lt;p&gt;Reinventing the wheel is a bad idea. It only makes sense when learning to code or a new concept. Understand something, disassemble it, then recreate a simpler version. In fact, &lt;a href=&quot;/2024/12/31/GetBetterAtCoding/&quot;&gt;that’s the best way to learn coding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The full quote should be “don’t reinvent the wheel. Only reinvent it to know how it works. Then use a real wheel.”&lt;/p&gt;

&lt;p&gt;“You can only recreate what you understand” works in the other direction too. You only understand what you can recreate. You only understand the wheel you reinvent.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>In Case of Writer&apos;s Block, Break the Glass and Follow These Prompts</title>
   <link href="https://canro91.github.io/2025/02/22/WritingPrompts/"/>
   <updated>2025-02-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/22/WritingPrompts</id>
   <content type="html">&lt;p&gt;It took me almost half a day to come up with yesterday’s subject.&lt;/p&gt;

&lt;p&gt;That was after claiming, a few days ago, that &lt;a href=&quot;/2025/02/16/ForgetWritersBlock/&quot;&gt;writer’s block is not the real problem&lt;/a&gt;. Well, it took me more than usual to mark the calendar yesterday, even after looking for inspiration in past posts and half-baked ideas. I ended up writing about one of my recent frustrations: &lt;a href=&quot;/2025/02/20/HourlyBillingIsNuts/&quot;&gt;Hourly billing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In case you’re like me yesterday, struggling to find what to write about, here’s a list of prompts to help you write today.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Expand an old post.&lt;/li&gt;
  &lt;li&gt;Dissect a book passage.&lt;/li&gt;
  &lt;li&gt;Describe what’s around you.&lt;/li&gt;
  &lt;li&gt;Turn &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;a 10-idea list&lt;/a&gt; into a post.&lt;/li&gt;
  &lt;li&gt;Write about your favorite quote.&lt;/li&gt;
  &lt;li&gt;Summarize your favorite podcast episode.&lt;/li&gt;
  &lt;li&gt;Write a letter to your past or future self.&lt;/li&gt;
  &lt;li&gt;Expand one of your comments on social media into a post.&lt;/li&gt;
  &lt;li&gt;Answer a question from Reddit, Quora, or any other forum.&lt;/li&gt;
  &lt;li&gt;Set a timer and write about whatever comes to your mind.&lt;/li&gt;
  &lt;li&gt;Open your browser history and compile the answers you found.&lt;/li&gt;
  &lt;li&gt;Scroll through YouTube or any other feed and write a reaction post.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even if you don’t have anything to write, don’t miss two days without practicing. Your writing muscle will atrophy. And here’s a &lt;a href=&quot;/2025/02/07/WritingTasks/&quot;&gt;list of tasks for the days you don’t feel like writing&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Stopped Listening to Free Advice and Started Doing This Instead</title>
   <link href="https://canro91.github.io/2025/02/21/StopListeningToFreeAdvice/"/>
   <updated>2025-02-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/21/StopListeningToFreeAdvice</id>
   <content type="html">&lt;p&gt;Too much free Internet advice feels daunting.&lt;/p&gt;

&lt;p&gt;I’ve gone all in on my writing skills since last year. And I’ve found all kinds of advice. &lt;a href=&quot;/2024/12/26/WriteDaily/&quot;&gt;Write daily&lt;/a&gt;. Don’t write daily…Use AI. &lt;a href=&quot;/2025/02/15/FightingAIContent/&quot;&gt;Don’t use AI&lt;/a&gt;…Sell more. Educate more…Find a niche. Don’t find a niche…Do this. Don’t do that. Arrggg!&lt;/p&gt;

&lt;p&gt;Not all advice is suited for everyone. Not everyone is at the same stage of the journey.&lt;/p&gt;

&lt;p&gt;The other day, someone tried to sell me a course to optimize sales funnels, when I didn’t have anything to sell yet. Good advice, bad timing. The same happens with most advice we get.&lt;/p&gt;

&lt;p&gt;Instead of taking advice from anyone and everyone at the same time, follow someone who has achieved the same results you want, listen, test, and adapt their advice.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Do Yourself a Favor and Stop Charging by the Hour</title>
   <link href="https://canro91.github.io/2025/02/20/HourlyBillingIsNuts/"/>
   <updated>2025-02-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/20/HourlyBillingIsNuts</id>
   <content type="html">&lt;p&gt;“I have developers who charge me $18 per hour. Your fee is way more expensive.”&lt;/p&gt;

&lt;p&gt;I was on the phone with my first lead, a small business owner looking for a software engineer. A friend connected us. I was trying to start freelancing in my local market. And if I got a yes, I was about to become the subcontractor of a subcontractor.&lt;/p&gt;

&lt;p&gt;I gave him a flat fee.&lt;/p&gt;

&lt;p&gt;I had already heard about the dangers of charging by the hour. I had watched a couple of YouTube videos and learned a sales script.&lt;/p&gt;

&lt;p&gt;But on the other side of the phone, he divided my flat fee by the estimated hours to completion he had. And he tried to persuade me to give him an hourly rate and an hourly rate as low as the one he already had.&lt;/p&gt;

&lt;p&gt;Hourly billing is nuts. Here’s why.&lt;/p&gt;

&lt;h2 id=&quot;1-hourly-billing-doesnt-encourage-productivity&quot;&gt;1. Hourly billing doesn’t encourage productivity.&lt;/h2&gt;

&lt;p&gt;The better I become at my work, the faster I do it. And if I charge by the hour, the faster I do my work, the less money I make.&lt;/p&gt;

&lt;p&gt;Sure, I could raise my hourly rates, but that would put me in a race to the bottom. “I have developers who charge me $18 per hour,” he said.&lt;/p&gt;

&lt;h2 id=&quot;2-what-should-i-charge-for&quot;&gt;2. What should I charge for?&lt;/h2&gt;

&lt;p&gt;There’s no clear line between billable work and non-billable work.&lt;/p&gt;

&lt;p&gt;If I set aside my time for that client, but I’m waiting for his input, should I charge for that? While I’m thinking or doing research, should I charge for that? What should I charge for? Only for the time I’m typing symbols on a page? Arrggg!&lt;/p&gt;

&lt;h2 id=&quot;3-hourly-billing-invites-micromanaging&quot;&gt;3. Hourly billing invites micromanaging.&lt;/h2&gt;

&lt;p&gt;“Hey, did it take you X hours to do that? I have people who do that way faster! Let’s revisit your timesheet.”&lt;/p&gt;

&lt;p&gt;And that’s how my freelancing coding business ended with one phone call. Do yourself a favor and don’t charge by the hour. &lt;a href=&quot;/2024/11/04/RaisingRates/&quot;&gt;I’ve forgotten that lesson and I regret it&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>20+ Signs You&apos;re a Real Programmer (Using 2 Monitors Isn&apos;t One)</title>
   <link href="https://canro91.github.io/2025/02/19/YouAreNotAProgrammerUntil/"/>
   <updated>2025-02-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/19/YouAreNotAProgrammerUntil</id>
   <content type="html">&lt;p&gt;Today I found a post claiming you’re not a senior software engineer until you work on a legacy app.&lt;/p&gt;

&lt;p&gt;It made me think about when we can call ourselves programmers.&lt;/p&gt;

&lt;h2 id=&quot;from-me&quot;&gt;From me:&lt;/h2&gt;

&lt;p&gt;So, you’re not a programmer until:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;You write a to-do app or a recipe catalog&lt;/li&gt;
  &lt;li&gt;You google &lt;a href=&quot;/2025/03/08/TipsForNewCoders/&quot;&gt;how to become a better coder&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You have an interview with a clueless recruiter&lt;/li&gt;
  &lt;li&gt;You copy and paste a code block from StackOverflow&lt;/li&gt;
  &lt;li&gt;You take down a database server with a badly written query&lt;/li&gt;
  &lt;li&gt;You read &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;the Clean Code&lt;/a&gt; and want to rewrite all the code around you&lt;/li&gt;
  &lt;li&gt;You debug using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Console.WriteLine&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.log&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;printf&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;You get a PM asking you how you’re doing more than once a day&lt;/li&gt;
  &lt;li&gt;You google your error message and find an open issue on GitHub&lt;/li&gt;
  &lt;li&gt;You delete a database table with a DELETE without WHERE&lt;/li&gt;
  &lt;li&gt;You argue about a variable name during a code review&lt;/li&gt;
  &lt;li&gt;You write a class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; when learning about OOP&lt;/li&gt;
  &lt;li&gt;You code a calculator app using JavaScript&lt;/li&gt;
  &lt;li&gt;You work on a full rewrite of a legacy app&lt;/li&gt;
  &lt;li&gt;You google how to center a div on a webpage&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;from-my-friends-and-ex-coworkers&quot;&gt;From my friends and ex-coworkers:&lt;/h2&gt;

&lt;p&gt;I asked some of my friends and ex-coworkers to complete that sentence. And here’s what they told me.&lt;/p&gt;

&lt;p&gt;You’re not a programmer until…&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You write your first “Hello, world” program&lt;/li&gt;
  &lt;li&gt;You stay awake until 3:00 AM solving a coding issue&lt;/li&gt;
  &lt;li&gt;You’re fixing an issue, it works and you don’t know why&lt;/li&gt;
  &lt;li&gt;Your code works on your machine, but not in Production&lt;/li&gt;
  &lt;li&gt;You get a compilation error on line 123 on a 40-line code file&lt;/li&gt;
  &lt;li&gt;You deploy a hotfix to Production at 17:55 (and you clock out at 18:00)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;from-devto&quot;&gt;From dev.to:&lt;/h2&gt;

&lt;p&gt;I reposted this on &lt;a href=&quot;https://dev.to/canro91/youre-not-a-programmer-until-58i&quot;&gt;dev.to&lt;/a&gt; and here are my favorites from the comments. (Slightly edited to make them fit in one line)&lt;/p&gt;

&lt;p&gt;You’re not a programmer until…&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You’ve programmed&lt;/li&gt;
  &lt;li&gt;You use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push --force --all&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;You say “Well, it works in Dev”&lt;/li&gt;
  &lt;li&gt;You have a folder of unfinished projects&lt;/li&gt;
  &lt;li&gt;You build a collection of most-used code snippets&lt;/li&gt;
  &lt;li&gt;You set your git username to “User” so they can’t blame you&lt;/li&gt;
  &lt;li&gt;You use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git reset --hard&lt;/code&gt; or delete your local repo and clone it again&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/10/TilPosts/&quot;&gt;You start a blog&lt;/a&gt; to share your learning to save others debugging time&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;You’re stuck on a bug&lt;/a&gt; and your code works when you call a senior for help&lt;/li&gt;
  &lt;li&gt;You’ve spent time installing &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;your favorite editor/IDE’s plugins and themes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You feel stupid by constantly trying to learn things you do not know what they’re for&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And you’re not truly a coder until you grab a copy of my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;OK, jokes aside, for so long I thought coding was only about cracking symbols. But I had to learn coding is a wide range of skills like surviving on-call rotations, talking to non-tech managers, and many more I included in my book.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;—That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Being Helpful Isn&apos;t Always Good Advice, Try This Instead</title>
   <link href="https://canro91.github.io/2025/02/18/BeingTooHelpful/"/>
   <updated>2025-02-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/18/BeingTooHelpful</id>
   <content type="html">&lt;p&gt;We all want to stand out at work.&lt;/p&gt;

&lt;p&gt;We all want a place at the table, a voice in important decisions. OK, maybe not all of us, but it’s a good thing to be heard and known.&lt;/p&gt;

&lt;p&gt;These days, while preparing my &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;next Friday Links email&lt;/a&gt;, I found &lt;a href=&quot;https://newsletter.eng-leadership.com/p/15-lessons-from-15-years-in-tech&quot;&gt;this post&lt;/a&gt; with 15 lessons from 15 years of coding. It has good advice and advice I’d take with a pinch of salt, like this one:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;#6. Be Helpful First&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;Want to accelerate your career? Focus on helping others succeed. Thoughtfully review pull requests, ask your manager what they need help with, jump in when someone’s stuck, and freely share knowledge.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sure, when you help, people notice you. Bosses notice you. And being noticed means salary increases and new roles.&lt;/p&gt;

&lt;h2 id=&quot;but-be-careful-with-being-too-helpful&quot;&gt;But, be careful with being too helpful.&lt;/h2&gt;

&lt;p&gt;When you’re too helpful:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;You miss your own deadlines and get into trouble.&lt;/li&gt;
  &lt;li&gt;You become the go-to person or a hero.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you’re the go-to person, or the only one who does or knows how to do something, you’re a hero.&lt;/p&gt;

&lt;p&gt;A hero can’t get sick, go on vacations, take time off, or be promoted. Because if you’re the only one knowing or doing, who’s going to know or do it while you’re out? You can’t leave.&lt;/p&gt;

&lt;p&gt;A hero is stuck.&lt;/p&gt;

&lt;p&gt;A hero gets phone calls in the middle of the night or on weekends. I don’t know about you, but I definitely don’t enjoy calls from work on my weekends. I don’t want to be a hero.&lt;/p&gt;

&lt;p&gt;Instead of being a hero, be a team player.&lt;/p&gt;

&lt;p&gt;A team player documents, automates, and teaches what they do and know how to do. A team player can leave at any time without worries. A team player isn’t stuck.&lt;/p&gt;

&lt;p&gt;That’s a better way to stand out than being too helpful or a hero.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Become Time Billionaire</title>
   <link href="https://canro91.github.io/2025/02/17/TimeBillionaire/"/>
   <updated>2025-02-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/17/TimeBillionaire</id>
   <content type="html">&lt;p&gt;True wealth isn’t just about money.&lt;/p&gt;

&lt;p&gt;Apart from money, wealth also means health and freedom. Physical, emotional, and spiritual health. And freedom to choose to work on what you want, when you want, surrounded by people you want.&lt;/p&gt;

&lt;p&gt;That was what Dan Koe and Sahil Bloom talked about during the next interview:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/vG4dnVJTA9w?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Here are 10 lessons I learned from that conversation:&lt;/p&gt;

&lt;p&gt;#1. &lt;strong&gt;Not all time is equal.&lt;/strong&gt; When you say “yes” to business or work, chances are you’re saying “no” to time with your loved ones.&lt;/p&gt;

&lt;p&gt;#2. &lt;strong&gt;Imagine your funeral and ask yourself, who would be there?&lt;/strong&gt; It might sound creepy, but start investing in your relationships. The simplest way to do that is by texting your friends when you remember them.&lt;/p&gt;

&lt;p&gt;#3. &lt;strong&gt;Audit your calendar for energy creators and energy drainers.&lt;/strong&gt; Find what makes you feel alive and what does not. Double down on your energy creators. And if you can’t avoid your energy drainers, batch them.&lt;/p&gt;

&lt;p&gt;#4. &lt;strong&gt;Entrepreneurship is creating.&lt;/strong&gt; It’s about finding a problem, coming up with a solution, and scaling that solution. And you don’t have to leave your full-time job to be an entrepreneur.&lt;/p&gt;

&lt;p&gt;#5. &lt;strong&gt;Master storytelling and sales.&lt;/strong&gt; Life is about sales. We’re constantly negotiating and selling, since we’re kids.&lt;/p&gt;

&lt;p&gt;#6. Having a hard time niching down? &lt;strong&gt;Find something you’d stick to for 5-10 years.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;#7. &lt;strong&gt;Question the things you “have” to.&lt;/strong&gt; Question the default path. Create your own path.&lt;/p&gt;

&lt;p&gt;#8. &lt;strong&gt;Define your enough life.&lt;/strong&gt; How does your ideal day look like? When will you stop chasing the next step? The next promotion? The next milestone? What’s your enough?&lt;/p&gt;

&lt;p&gt;#9. &lt;strong&gt;Use 30 minutes to 1 hour to create the life you want.&lt;/strong&gt; If you can work 8 hours to make somebody else’s dream come true, you can work 1 hour to build yours.&lt;/p&gt;

&lt;p&gt;#10. &lt;strong&gt;Have a Life Dinner with your partner or with yourself.&lt;/strong&gt; Use that time to reflect and recalibrate.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“You’ll achieve more by being consistently reliable than by being occasionally extraordinary”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Better than being just a millionaire is to become a time millionaire, prioritizing what truly matters to you and living under your own terms.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Forget Writer&apos;s Block: Here&apos;s the Real Problem and How to Fix It</title>
   <link href="https://canro91.github.io/2025/02/16/ForgetWritersBlock/"/>
   <updated>2025-02-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/16/ForgetWritersBlock</id>
   <content type="html">&lt;p&gt;Nothing is scarier than a blank page, but what if the problem isn’t writer’s block?&lt;/p&gt;

&lt;p&gt;If you stare at a blank page with nothing to write, you’re not suffering from writer’s block, but from the lack of an idea-capturing system.&lt;/p&gt;

&lt;p&gt;Here’s how to fix it.&lt;/p&gt;

&lt;h2 id=&quot;1-garbage-in-garbage-out&quot;&gt;1. Garbage in, garbage out&lt;/h2&gt;

&lt;p&gt;You are the food you eat and the content you consume.&lt;/p&gt;

&lt;p&gt;Of course, if you’re mindlessly scrolling TikTok, you won’t have anything to write about.&lt;/p&gt;

&lt;p&gt;Instead, be conscious of what you consume. Read more. Read fiction and non-fiction. Read anything that crosses your path. That’s your source of inspiration.&lt;/p&gt;

&lt;p&gt;And apart from inspiring you, being exposed to good writing is &lt;a href=&quot;/2025/01/20/WritingToLearn/&quot;&gt;the best way to learn to write&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-have-a-capturing-system&quot;&gt;2. Have a capturing system&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/26/ContentIsEverywhere/&quot;&gt;Content is everywhere&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From comments in social media to quotes to TV shows to conversations to book passages. Everything and anything that happens around you is full of content ideas. In fact, this post started as a comment on social media.&lt;/p&gt;

&lt;p&gt;Have a place to store and organize those ideas: Your phone, pen and paper, sticky notes, &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;plain-text files&lt;/a&gt;, a stone and chisel. Anywhere.&lt;/p&gt;

&lt;p&gt;Since last year, I’ve been following &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;James Altucher’s Idea Machine&lt;/a&gt; concept. After consuming (almost) anything, I write 10 ideas or lessons I learned from that. Usually those 10 ideas become posts.&lt;/p&gt;

&lt;p&gt;Notice content ideas around you and don’t let them go.&lt;/p&gt;

&lt;h2 id=&quot;3-dont-try-to-come-up-with-anything-original&quot;&gt;3. Don’t try to come up with anything original&lt;/h2&gt;

&lt;p&gt;There’s nothing new under the sun.&lt;/p&gt;

&lt;p&gt;That’s from Ecclesiastes, from the Bible, from thousands of years ago. There wasn’t anything new back then. And there isn’t now. We’ve been adding, subtracting, and remixing ideas since then.&lt;/p&gt;

&lt;p&gt;You don’t have to come up with original ideas to write. If you find somebody else’s idea (remember #2?), you can make it yours by sharing it from your perspective, along with a personal story.&lt;/p&gt;

&lt;p&gt;Consume, capture, and remix ideas and you will always have something to write about. If you still think you have nothing to write, &lt;a href=&quot;/2024/12/07/WritersBlock/&quot;&gt;follow this tip&lt;/a&gt;. And no, you will &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;never run out of ideas to write&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We&apos;ll Lose the Battle Against AI-Generated Content. Here&apos;s What to Do Instead</title>
   <link href="https://canro91.github.io/2025/02/15/FightingAIContent/"/>
   <updated>2025-02-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/15/FightingAIContent</id>
   <content type="html">&lt;p&gt;“In the realm of…” “In the world of…” “In our fast-paced world”&lt;/p&gt;

&lt;p&gt;AI-generated content floods the Internet. And it’ll only get worse as AI tools become better.&lt;/p&gt;

&lt;p&gt;That’s not new. We’ve had mediocre content flooding the Internet since always with spam and posts playing SEO schemes.&lt;/p&gt;

&lt;p&gt;I write on dev.to, and I still find posts full of keywords pretending to rank higher in the search results. SEO is dead. Well, that’s what everybody claims these days.&lt;/p&gt;

&lt;p&gt;We have to choose our battles.&lt;/p&gt;

&lt;p&gt;And we’ll lose the one against AI-generated content. Every day we have more and better AI tools that generate mediocre content in seconds. A 1,000-word post? Beep, beep, boop. Boom! Here you have it!&lt;/p&gt;

&lt;p&gt;If you’re writing anywhere online, complaining about AI-generated content won’t do anything. Complaining in general doesn’t do anything.&lt;/p&gt;

&lt;p&gt;From &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, one of my favorite writers, I’ve learned that:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If AI can write it, you need to rewrite it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to stand out in an ocean of AI-generated content, you have to do what AI can’t do yet: add your personal touch, experiences, anecdotes, stories, and bad jokes. You have to make your content more human. &lt;a href=&quot;/2024/12/24/WritingVoice/&quot;&gt;You have to find your voice&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AI is going to kill mediocre writers, but it’ll make good writers stand out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Writing For Developers: Takeaways and an Honest Review</title>
   <link href="https://canro91.github.io/2025/02/14/WritingForDevelopers/"/>
   <updated>2025-02-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/14/WritingForDevelopers</id>
   <content type="html">&lt;p&gt;Like coding, writing is everywhere in software projects.&lt;/p&gt;

&lt;p&gt;From README files to use cases to bug reports to blog posts. We developers spend as much time writing as coding, if not more.&lt;/p&gt;

&lt;p&gt;Chances are if you’ve been stuck with an error for hours, some random guy’s blog post has saved your day.&lt;/p&gt;

&lt;p&gt;Writing for Developers shows how to write those technical (or engineering) blog posts.&lt;/p&gt;

&lt;p&gt;Here’s my honest review and key takeaways:&lt;/p&gt;

&lt;h2 id=&quot;youre-not-writing-enough-and-other-takeaways&quot;&gt;“You’re not writing enough” and other takeaways&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. Technical blog posts don’t have to be boring.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They should have a balance between an interesting subject, a valuable lesson, and good packaging for the reader. And after reading your posts, your readers should leave with something learned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. If you find a subject interesting, other people will find it too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Write about anything you find interesting.&lt;/p&gt;

&lt;p&gt;But if you’re writing for the first time and you’re not sure what to write about, remember 3 Ps:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;what you’re passionate about&lt;/li&gt;
  &lt;li&gt;what you’re pained by&lt;/li&gt;
  &lt;li&gt;what you’re proud of&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;#3. We shouldn’t rely on AI to write our posts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But, it doesn’t mean we can’t use it to critique or review our drafts.&lt;/p&gt;

&lt;p&gt;Here are two prompts to critique our drafts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;“Are there any statements in this engineering blog post that do not seem adequately supported by facts?”&lt;/li&gt;
  &lt;li&gt;“Can you identify any parts of this article that seem less relevant to its main goal of [your goal]?”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My favorite lesson? “You’re not writing enough.” You can always write more.&lt;/p&gt;

&lt;h2 id=&quot;my-alternative-how-to-read-this-book&quot;&gt;My alternative “how to read this book”&lt;/h2&gt;

&lt;p&gt;Writing is not a subject you will learn from a single book.&lt;/p&gt;

&lt;p&gt;It takes years to master it. And we, as developers, have a double challenge: putting words together and presenting complex subjects clearly.&lt;/p&gt;

&lt;p&gt;If you’re new to writing, you will get more out of this book by reading the first two parts, especially chapters 4 and 5. They cover how to write and polish your first draft.&lt;/p&gt;

&lt;p&gt;This book doesn’t answer where to write. It slightly covers how to do it in an appendix. If you’re writing for your company’s engineering blog, they likely have already sorted out those details. Hopefully.&lt;/p&gt;

&lt;p&gt;Now if you’re not new to writing, let’s say you already have a blog and have published more than a dozen blog posts, you will get more out of this book by reading Part 3.&lt;/p&gt;

&lt;p&gt;Part 3 covers 7 blog post patterns:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Bug Hunt&lt;/li&gt;
  &lt;li&gt;Rewrote it in X&lt;/li&gt;
  &lt;li&gt;How We Built it&lt;/li&gt;
  &lt;li&gt;Lessons Learned&lt;/li&gt;
  &lt;li&gt;Thoughts on Trends&lt;/li&gt;
  &lt;li&gt;Non-markety Product Perspectives&lt;/li&gt;
  &lt;li&gt;Benchmarks and Test results&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each chapter contains sample blog posts, a commentary on those posts, and an explanation of how to write each type of post.&lt;/p&gt;

&lt;p&gt;That was my favorite part.&lt;/p&gt;

&lt;p&gt;Definitely I’ve written some of those type of posts. But I didn’t think of them as patterns we could implement.&lt;/p&gt;

&lt;p&gt;My final verdict? 3.8/5.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: AutoMapper Only Considers Simple Mappings When Validating Configurations</title>
   <link href="https://canro91.github.io/2025/02/13/AutoMapperValidations/"/>
   <updated>2025-02-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/13/AutoMapperValidations</id>
   <content type="html">&lt;p&gt;Oh boy! AutoMapper once again.&lt;/p&gt;

&lt;p&gt;Today I have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateMovieRequest&lt;/code&gt; with a boolean property &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICanWatchItWithKids&lt;/code&gt; that I want to map to a MPA rating. If I can watch it with kids, the property &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MPARating&lt;/code&gt; on the destination type should get a “General” rating. Anything else gets “Restricted.”&lt;/p&gt;

&lt;p&gt;To my surprise, this test fails:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AutoMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TestProject1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WhyAutoMapperWhy&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateMovieRequest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ICanWatchItWithKids&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Sure, there are more.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// But these two are enough.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;General&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Restricted&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AutoMapperConfig_IsValid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapperConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapperConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMovieRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MemberList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanWatchItWithKids&lt;/span&gt;
                                                        &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;General&lt;/span&gt;
                                                        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Restricted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;mapperConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AssertConfigurationIsValid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//           ^^^^^														&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// AutoMapper.AutoMapperConfigurationException:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// CreateMovieRequest -&amp;gt; Movie (Source member list)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// TestProject1.WhyAutoMapperWhy+CreateMovieRequest -&amp;gt; TestProject1.WhyAutoMapperWhy+Movie (Source member list)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Unmapped properties:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ICanWatchItWithKids&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It turns out that starting from AutoMapper version 10.0, only source members expressions are considered when validating mappings. And it’s buried in the &lt;a href=&quot;https://docs.automapper.org/en/latest/10.0-Upgrade-Guide.html#source-validation&quot;&gt;Upgrade Guide here&lt;/a&gt;. Arrggg!&lt;/p&gt;

&lt;h2 id=&quot;two-solutions-one-for-the-lazy-and-the-right-one&quot;&gt;Two solutions: One for the lazy and the right one&lt;/h2&gt;

&lt;p&gt;Since I’m validating mappings based on the source type, I can simply ignore it:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMovieRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MemberList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanWatchItWithKids&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;General&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Restricted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForSourceMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanWatchItWithKids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoNotValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Thanks AutoMapper, I&apos;ll take it from here.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It feels like cheating, but it works.&lt;/p&gt;

&lt;p&gt;Or, I can use a converter:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMovieRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MemberList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConvertUsing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                       &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                       &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromBoolToMPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                       &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ICanWatchItWithKids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// And here&apos;s the converter:&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FromBoolToMPARating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IValueConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResolutionContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Here&apos;s the actual mapping:		&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceMember&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;General&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MPARating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Restricted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another day working with AutoMapper. It would have been way easier mapping that by hand.&lt;/p&gt;

&lt;p&gt;For more adventures with AutoMapper, check &lt;a href=&quot;/2025/01/24/IgnoringPropertiesAutoMapper/&quot;&gt;How to ignore unmapped fields in the destination type&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What Happened Around This Day In My Blog</title>
   <link href="https://canro91.github.io/2025/02/12/AroundThisDay/"/>
   <updated>2025-02-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/12/AroundThisDay</id>
   <content type="html">&lt;p&gt;A bit of nostalgia, reflection, and compilation today.&lt;/p&gt;

&lt;p&gt;This is what happened, not today, but around this day in my blog:&lt;/p&gt;

&lt;p&gt;Last year, before &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;being laid off&lt;/a&gt;, I had just finished another short project with a contracting client. It was a project to support events, like weddings and conferences, in a hotel management software. That project taught me more about leadership and project management than coding. I wrote &lt;a href=&quot;/2024/02/05/LessonsOnAFinishedProject/&quot;&gt;two lessons I learned from that project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In 2023, I started a 4-part series on NullReferenceException with a post about &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;when NullReferenceException is thrown and how to avoid it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In 2022, I was still running Monday Links here. I wrote about &lt;a href=&quot;/2022/02/14/MondayLinks/&quot;&gt;Daily Meetings, Estimates, and Challenges&lt;/a&gt;. From that episode, I included a post I found with 5 signs to look for when it’s time to quit your job.&lt;/p&gt;

&lt;p&gt;By the way, these days, I’m not running Monday Links, but Friday Links on an email list. Every Friday, you’ll get a 2-minute email with curated resources about programming and software engineering. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;Join here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In 2021, I was working with a Stripe-powered payment processor. And I wrote about how to use &lt;a href=&quot;/2021/02/10/DecoratorPattern/&quot;&gt;the Decorator pattern&lt;/a&gt; to implement retry logic when sending payments to Stripe. The analogy I used to explain the Decorator pattern was IronMan wearing the HULKBUSTER suit.&lt;/p&gt;

&lt;p&gt;In 2020, I used &lt;a href=&quot;/2020/02/14/PipelinePattern/&quot;&gt;the Pipeline pattern&lt;/a&gt; to create a pipeline of steps to make reservations. And since I had already used it to create invoices and notes in an invoicing system, I decided to write about it.&lt;/p&gt;

&lt;p&gt;In 2019 and 2018, I didn’t write in February. It’s a shame. Well, I was still trying to find my rhythm and what to write about.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Full Rewrites Are Bad But Be Ready For Them</title>
   <link href="https://canro91.github.io/2025/02/11/RewritesAreBad/"/>
   <updated>2025-02-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/11/RewritesAreBad</id>
   <content type="html">&lt;p&gt;Rewriting legacy code that works isn’t a good idea.&lt;/p&gt;

&lt;p&gt;At some point in the rewrite, we have to maintain two systems. Fixing bugs in the legacy one while making sure they’re fixed in the new one too.&lt;/p&gt;

&lt;p&gt;These days, Antirez, Redis’ creator, wrote in &lt;a href=&quot;https://antirez.com/news/145&quot;&gt;We are destroying software&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We are destroying software pushing for rewrites of things that work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rewrites are expensive and time-consuming. Every rewrite starts with good intentions, but they all end up being the next application to rewrite.&lt;/p&gt;

&lt;p&gt;Interestingly enough, I’ve been involved in full rewrites at every job I’ve had.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;At my first job&lt;/a&gt;, I had to rewrite a WebForms app into a WinForms app. That was the solution to a networking issue. By the time I left (actually, &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;I was fired&lt;/a&gt;), there was an ongoing project to unify every separate department application into a single “unified” platform. Another rewrite.&lt;/p&gt;

&lt;p&gt;At my second job, similar story. A WebForms app backed by 1,000-LOC stored procedures. We rewrote it using ASP.NET Web API with NHibernate. But, by the time the president (a coder in a past life) found out that NHibernate was slower, he ordered another rewrite. A rewrite in the middle of another rewrite. It took us around five years to finish the first prototype of a working app.&lt;/p&gt;

&lt;p&gt;My next job rewrite was from Python jobs to background jobs with ASP.NET Core. The idea was to read a database table with price updates for rooms at a hotel and call the Expedia or Booking.com API. The challenge? We couldn’t send two price updates for the same hotel at the same time. These days I’d &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;use Hangfire&lt;/a&gt; for that.&lt;/p&gt;

&lt;p&gt;Next job? Rewriting a VisualBasic WebForm app into Blazor. Oh boy!&lt;/p&gt;

&lt;p&gt;You see? Chances are you can’t run away from rewrites and legacy code. And funny enough, “Working Effectively with Legacy Code” has been sitting in my to-read list all these years.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Start Selling Without Sales Calls</title>
   <link href="https://canro91.github.io/2025/02/10/SellingWithoutCalls/"/>
   <updated>2025-02-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/10/SellingWithoutCalls</id>
   <content type="html">&lt;p&gt;To stand out, do what everybody else isn’t doing.&lt;/p&gt;

&lt;p&gt;That’s a lesson I learned from &lt;a href=&quot;/2025/01/16/SpainTopCopywriter/&quot;&gt;Isra Bravo, Spain’s top copywriter&lt;/a&gt;, and maybe the country’s top solopreneur.&lt;/p&gt;

&lt;p&gt;When everybody is doing sales calls, putting chatbots on their websites, or offering personalized demos, it’s time to stand out by doing something completely different.&lt;/p&gt;

&lt;h2 id=&quot;this-company-ditched-the-book-a-call-button&quot;&gt;This company ditched the “Book a call” button&lt;/h2&gt;

&lt;p&gt;Recently, I discovered Keygen, a licensing software and &lt;a href=&quot;https://keygen.sh/blog/no-calls/&quot;&gt;its no calls policy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The founder, probably being an introvert, wanted to ditch sales calls and in a moment of frustration, he deleted the “Book a call” button from his company’s website.&lt;/p&gt;

&lt;p&gt;Instead of sales calls, they improved their messaging. They clarified what their tool does and how much it costs.&lt;/p&gt;

&lt;p&gt;And when someone requests a sales call, they simply reply:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“No, we don’t do calls, but happy to help via email. Feel free to CC any relevant team members onto this thread.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of Keygen’s prospects loved the #nocalls policy. Too many calls are boring, draining, and time-consuming.&lt;/p&gt;

&lt;h2 id=&quot;when-you-sell-to-the-right-people-youre-not-leaving-money-on-the-table&quot;&gt;When you sell to the right people, you’re not leaving money on the table&lt;/h2&gt;

&lt;p&gt;Sure, they might be leaving money on the table. But we don’t want money from just anyone, only from the right clients. The ones that trust us, respect us, and pay the right price.&lt;/p&gt;

&lt;p&gt;With a clear message and open pricing policies, we attract the right clients. We attract clients based on the messaging and the values we project. And that works for anything else in life.&lt;/p&gt;

&lt;p&gt;#nocalls is an idea worth copying and stealing, especially if selling feels draining and if you prefer to think of in terms of &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;helping, not selling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Better messaging = more sales.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Main Benefit of a Daily Writing Habit: 100 Daily Posts Reflection</title>
   <link href="https://canro91.github.io/2025/02/09/100DailyPosts/"/>
   <updated>2025-02-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/09/100DailyPosts</id>
   <content type="html">&lt;p&gt;Imagine writing a blog post every day for over 20 years.&lt;/p&gt;

&lt;p&gt;Seth Godin has done just that. A post a day for over 20 years means more than 7,000 daily blog posts. That’s more than impressive. I’ve always admired Seth’s blogging rhythm.&lt;/p&gt;

&lt;p&gt;Inspired by Seth Godin and other daily writers like Tim Denning and &lt;a href=&quot;/2024/09/16/LessonsFromHerbertLuiBlog/&quot;&gt;Herbert Lui&lt;/a&gt;, I started writing daily on my blog in November 2024. I even created a new tag, &lt;a href=&quot;/tags/misc&quot;&gt;/misc&lt;/a&gt;, for everything outside of software engineering and coding. That’s part of my &lt;a href=&quot;/2025/01/27/TimesOfDeadBlogging/&quot;&gt;strategy in these days of dead blogging&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For my daily writing challenge, one headline and one main idea are enough to call it a day. No cover image. No need for 10 points. If it’s longer than a tweet, it’s OK to publish. Around 200 words is my sweet spot.&lt;/p&gt;

&lt;p&gt;A daily writing habit has made me more conscious of the content I consume.&lt;/p&gt;

&lt;p&gt;When we set the intention to write daily, &lt;a href=&quot;/2025/01/26/ContentIsEverywhere/&quot;&gt;we start to see content everywhere&lt;/a&gt;: conversations, quotes, passages from books, podcast episodes, comments on social media, TV shows…“Oh, that could be a post, I’d better write down that idea.”&lt;/p&gt;

&lt;p&gt;My goal was to publish 100 daily posts starting on November 1st. This is my daily post #100. Mission accomplished.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Often You Don&apos;t Want It, but You Become a Leader—By Omission</title>
   <link href="https://canro91.github.io/2025/02/08/LeaderByOmission/"/>
   <updated>2025-02-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/08/LeaderByOmission</id>
   <content type="html">&lt;p&gt;One day, you’re the only team member and the next, you’re a leader, without asking for it.&lt;/p&gt;

&lt;p&gt;Been there and done that.&lt;/p&gt;

&lt;p&gt;Back in one of my first jobs, at some point, I was the only one in the mobile team.&lt;/p&gt;

&lt;p&gt;We were building the prototype of a mobile app with Xamarin. I don’t know if that’s still a thing. When there was too much work for me, two team members joined from other teams. They were learning Xamarin and getting up to speed with the business domain.&lt;/p&gt;

&lt;p&gt;And the next day, I started to be responsible for their work. Management asked me, not them, when they were behind schedule. I thought my job was only answering their questions and offering help. Nope!&lt;/p&gt;

&lt;p&gt;I wasn’t a leader and I didn’t want to be one. But that day, I became their leader by omission.&lt;/p&gt;

&lt;p&gt;And recently, it has happened to me again.&lt;/p&gt;

&lt;p&gt;But this time, I was on the other side of the table. I joined a small agency and got assigned to a project with only one team member. He was a lone wolf, working on his own corner with zero dependencies and shared work. The challenge came when we needed to sync our work and keep an eye on dependencies between our tasks. He was so used to working alone, and I was expecting more collaboration.&lt;/p&gt;

&lt;p&gt;When you’re the most senior member or the only team member, as soon as new pairs of hands come, you become the point of contact, the go-to person for questions and problems. Upper management starts to ask you about the new team members. You don’t ask for it, but you become a leader. A leader by omission. And remember &lt;a href=&quot;/2025/01/08/BeingATeamLeader/&quot;&gt;as a leader, you’re not the best coder anymore&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Productive Tasks for the Days You Don&apos;t Feel Like Writing</title>
   <link href="https://canro91.github.io/2025/02/07/WritingTasks/"/>
   <updated>2025-02-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/07/WritingTasks</id>
   <content type="html">&lt;p&gt;Like any other form of art, writing requires daily practice.&lt;/p&gt;

&lt;p&gt;Seth Godin has been doing it daily for over 20 years. Daily! That’s more than 7,000 posts.&lt;/p&gt;

&lt;p&gt;But I know, life throws us curveballs. Unexpected things happen. And some days, writing feels impossible. On those days, we feel like letting the chain break.&lt;/p&gt;

&lt;p&gt;There are still writing-related tasks (that are not writing) we can do on those days. Here are 10:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Proofread and edit future posts.&lt;/li&gt;
  &lt;li&gt;Find cover images or create visuals.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/12/17/BetterHeadlines/&quot;&gt;Steal headlines&lt;/a&gt;, hooks, and opening lines.&lt;/li&gt;
  &lt;li&gt;Repost top-performing posts in social media.&lt;/li&gt;
  &lt;li&gt;Hand-write book passages (or posts) from your favorite writers.&lt;/li&gt;
  &lt;li&gt;Audit posts from past days: What headlines did you use? What type of visuals?&lt;/li&gt;
  &lt;li&gt;Collect your most liked comments and turn them into separate posts or headlines.&lt;/li&gt;
  &lt;li&gt;Tweak low-performing posts: Create new visuals, change headlines, or add new stories.&lt;/li&gt;
  &lt;li&gt;Turn long-form content into short-form content or vice-versa: Blog posts or newsletters into Tweets or LinkedIn posts.&lt;/li&gt;
  &lt;li&gt;Repurpose top-performing posts: Turn a blog post into a LinkedIn carousel or create quote images from it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even if you don’t write daily, at least do anything writing-related. But never miss two days in a row. Otherwise, like any other muscle, your writing muscles will atrophy.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Essential First Steps for Unit Testing in C#</title>
   <link href="https://canro91.github.io/2025/02/06/UnitTestingFirstStep/"/>
   <updated>2025-02-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/06/UnitTestingFirstStep</id>
   <content type="html">&lt;p&gt;Today I got a question from Pierre on my contact page about unit testing. Here’s an edited version of his question:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;I’m stuck at square one: how do you invoke the Unit Tests? Where are the tests stored? Are they compiled into the Release code? Is there one function that calls the whole cascade of unit tests? Nobody seems to address this very basic, indispensable first step.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I slightly covered some of those questions on my post to &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;write your first unit tests with MSTest&lt;/a&gt;. I tried to write a beginner-friendly introduction to unit testing. But, Pierre is right. I forgot to answer those questions.&lt;/p&gt;

&lt;p&gt;So here I go:&lt;/p&gt;

&lt;h2 id=&quot;1-are-unit-tests-compiled-into-our-release-code&quot;&gt;1. Are unit tests compiled into our Release code?&lt;/h2&gt;

&lt;p&gt;First things first. We don’t ship unit tests to our end users. And, no. They’re not compiled into our release code.&lt;/p&gt;

&lt;p&gt;We write unit tests as part of our development process. Let’s say unit tests are code written for developers by developers.&lt;/p&gt;

&lt;p&gt;The best analogy? Writing unit tests is like crashing cars as part of their security tests in a factory. Well, car makers don’t ship crashed cars. Unit tests are like those crashed cars. We use them to test the cars we’re shipping to our users, but don’t ship them to our end users.&lt;/p&gt;

&lt;h2 id=&quot;2-where-to-put-our-unit-tests&quot;&gt;2. Where to put our unit tests&lt;/h2&gt;

&lt;p&gt;The convention I follow is to put unit tests in separate projects, named after the project where the code under test is, using “Tests” as a suffix.&lt;/p&gt;

&lt;p&gt;For example, if our code to test is under a project called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Acme.CoolProject&lt;/code&gt;, we should put our tests into a project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Acme.CoolProject.Tests&lt;/code&gt;. And inside the test project, we should use folders with the same name as the code under test.&lt;/p&gt;

&lt;p&gt;Now about the folder structure to put our tests, I’ve seen and used two options:&lt;/p&gt;

&lt;p&gt;#1. &lt;strong&gt;Using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; folders at the root of the solution&lt;/strong&gt;, like this&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Acme.CoolProject.sln
 |-src/
 |---Acme.CoolProject.Api/
 |-tests/
 |---Acme.CoolProject.Api.Tests/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;#2. &lt;strong&gt;Using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; folders inside folders per project&lt;/strong&gt;, like this&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Acme.CoolProject.sln
 |-Api/
 |---src/
 |-----Acme.CoolProject.Api/
 |---tests/
 |-----Acme.CoolProject.Api.Tests/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From the two options, the first one is the most common and it’s my go-to folder structure.&lt;/p&gt;

&lt;p&gt;For the actual tests names, I’ve found these &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;four test naming conventions&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-how-to-invoke-our-unit-tests&quot;&gt;3. How to invoke our unit tests&lt;/h2&gt;

&lt;p&gt;There are three options to invoke our tests—Again, we as developers, are the ones who invoke tests, not our end users:&lt;/p&gt;

&lt;p&gt;#1. &lt;strong&gt;Directly from your IDE&lt;/strong&gt;: Testing frameworks come with test runners that discover and run our tests. So we can simply press a button inside Visual Studio or our IDE to run one test, all tests inside a folder, or all the tests inside our solution.&lt;/p&gt;

&lt;p&gt;#2. &lt;strong&gt;From the command line&lt;/strong&gt;: Thanks to &lt;a href=&quot;/2022/12/15/CreateProjectStructureWithDotNetCli/&quot;&gt;.NET CLI&lt;/a&gt;, we can simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet test&lt;/code&gt; inside a folder to run all the tests inside that folder.&lt;/p&gt;

&lt;p&gt;#3. &lt;strong&gt;From a deployment tool&lt;/strong&gt;: If we’re using a continuous integration and deployment tool, like GitHub Actions or Jenkins, we can run our tests as part of the deployment workflow to make sure we’re not shipping broken code. These tools default to the .NET CLI to run the tests under the hood.&lt;/p&gt;

&lt;p&gt;So there’s no one function that calls the whole cascade of unit tests, but a button on your IDE.&lt;/p&gt;

&lt;p&gt;Et voilà! To read more about unit testing, don’t miss the rest of the series &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’ve included these answers and more lessons in my book &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UnitTesting101eBook-Intro&quot;&gt;Start Testing&lt;/a&gt;. A step-by-step guide to writing your first C# unit tests with confidence.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Essential Lessons for New Bloggers from a Viral Post</title>
   <link href="https://canro91.github.io/2025/02/05/LessonsFromAViralPost/"/>
   <updated>2025-02-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/05/LessonsFromAViralPost</id>
   <content type="html">&lt;p&gt;One of my posts recently went viral on dev.to.&lt;/p&gt;

&lt;p&gt;It was the one about &lt;a href=&quot;/2024/11/30/TestingPrivateMethods/&quot;&gt;why we shouldn’t try to test private methods&lt;/a&gt;. And here’s the &lt;a href=&quot;https://dev.to/canro91/this-is-why-we-dont-test-private-methods-28ef&quot;&gt;dev.to version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I say “viral,” I mean viral by my standards. It received +30 reactions, +30 comments, and +5K views. That was after two days of posting. Two days. It probably got syndicated or reshared somewhere else.&lt;/p&gt;

&lt;p&gt;It’s not that impressive for tech influencers with a larger follower count. But it is for me.&lt;/p&gt;

&lt;p&gt;Since I don’t want this to be a “see how cool I am” kind of post, here are 5 lessons I learned that I’d like to pass on to new bloggers:&lt;/p&gt;

&lt;h2 id=&quot;1-you-cant-predict-your-next-hit&quot;&gt;1. You can’t predict your next hit&lt;/h2&gt;

&lt;p&gt;No matter how hard you try, you can’t predict which post will go “viral.”&lt;/p&gt;

&lt;p&gt;Often the posts you write to mark the calendar are the ones that get some traction, not the ones you spend time crafting. This one was one of those. I wrote it to mark the calendar as part of my 100-daily-posts challenge I started in November 2024.&lt;/p&gt;

&lt;p&gt;Since you can’t predict your hits, keep writing, even if you think nobody is reading.&lt;/p&gt;

&lt;h2 id=&quot;2-go-with-social-blogs-to-get-more-traction&quot;&gt;2. Go with social blogs to get more traction&lt;/h2&gt;

&lt;p&gt;I wrote that post originally here on my blog in November 2024. Since then, it only got less than 10 views.&lt;/p&gt;

&lt;p&gt;But it went viral when I reposted it on dev.to. It was sitting on my blog behind search engines. I had to wait for search engine bots to crawl, index, and rank my post. And that could take weeks or months.&lt;/p&gt;

&lt;p&gt;On social blogs, like dev.to or Medium, there’s already an audience and a discoverability feature. Old-fashioned blogs don’t have that. That’s the problem with blogs.&lt;/p&gt;

&lt;p&gt;If you’re new to blogging or &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;struggling to keep writing&lt;/a&gt;, don’t start on your own blog. Go with a social blog. If you don’t know where to start, try with dev.to.&lt;/p&gt;

&lt;h2 id=&quot;3-always-promote-something-at-the-end-of-your-posts&quot;&gt;3. Always promote something at the end of your posts&lt;/h2&gt;

&lt;p&gt;These days, I have learned from &lt;a href=&quot;/2025/01/16/SpainTopCopywriter/&quot;&gt;Spain’s top copywriter&lt;/a&gt;, that an email should be like a radio show.&lt;/p&gt;

&lt;p&gt;A radio show has a bit of education, a bit of entertainment, and a promotion. And a post should be the same.&lt;/p&gt;

&lt;p&gt;Promote something at the end of your posts: your ebooks, newsletter, or courses. I got 7 downloads of my ebook &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot;&gt;Unit Testing 101: From Zero to Your First Tests&lt;/a&gt; on my Gumroad page thanks to that post.&lt;/p&gt;

&lt;p&gt;Promote something that aligns with the content of your posts. Share something of value, not just simply asking for a coffee.&lt;/p&gt;

&lt;p&gt;If you don’t have an ebook or something, promote other posts. If Netflix does the same at the end of a movie or show, we can do the same.&lt;/p&gt;

&lt;h2 id=&quot;4-remember-the-303030-rule&quot;&gt;4. Remember the 30/30/30 rule&lt;/h2&gt;

&lt;p&gt;My post got more than 30 comments.&lt;/p&gt;

&lt;p&gt;Some people jumped to support my idea. Others jumped to offer interesting alternatives. Others simply said I wasn’t answering anything. Others went on a tangent talking about 3D rendering with JavaScript. Somebody else jumped to promote a jewelry business or something. Whaaaat! Yes, a jewelry business on a coding platform. Whatever.&lt;/p&gt;

&lt;p&gt;No matter who you are or what you do, 30% will love it, 30% will hate it, and 30% won’t even care.&lt;/p&gt;

&lt;h2 id=&quot;5-answer-questions-in-public&quot;&gt;5. Answer questions in public&lt;/h2&gt;

&lt;p&gt;I didn’t come up with the subject of that post myself. I stole it.&lt;/p&gt;

&lt;p&gt;One day I was wasting my time on Reddit, probably waiting on a line or something, and I found that question. I realized I could answer it and I did so.&lt;/p&gt;

&lt;p&gt;It was a genuine question someone asked. Someone else will probably google the same question. And answering it was good in SEO terms for my blog. And that’s one of the tricks you can use to &lt;a href=&quot;/2025/01/28/DoubleYourPostCount/&quot;&gt;double your post count&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I Made Money Coding and How I&apos;d Do It If I Had to Start Again</title>
   <link href="https://canro91.github.io/2025/02/04/MakingMoneyCoding/"/>
   <updated>2025-02-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/04/MakingMoneyCoding</id>
   <content type="html">&lt;p&gt;These days, I got a question from Venkata on LinkedIn. Here’s an edited version of that question:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I need your guidance on how to turn my skills into an opportunity to earn income by solving a real world problem or project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s my answer. Of course, this is free Internet advice. Take it with a pinch of salt.&lt;/p&gt;

&lt;p&gt;I won’t tell you what to do. Let alone do I have a step-by-step formula. I’m telling you what I did and what I’d do if I had to start again.&lt;/p&gt;

&lt;p&gt;I’m assuming you’re a new graduate from university or a bootcamp with a few years of flight time.&lt;/p&gt;

&lt;h2 id=&quot;1-take-a-tour-of-the-corporate-world&quot;&gt;1. Take a tour of the corporate world&lt;/h2&gt;

&lt;p&gt;As crazy as it might sound, take a corporate job in a company in your city. Even if it’s at a boring non-tech company without a big name.&lt;/p&gt;

&lt;p&gt;A boring job in a non-tech company is not the dream destination. I know. When we all start, we dream about joining the next rising startup or a FAANG. But you don’t have to stay in the corporate world forever.&lt;/p&gt;

&lt;p&gt;In a corporate job:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You can get some training and learning opportunities.&lt;/li&gt;
  &lt;li&gt;You will learn to work with legacy codebases.&lt;/li&gt;
  &lt;li&gt;You can make mistakes on somebody else’s business with almost no consequences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;My first coding job&lt;/a&gt; was at a boring non-tech company. I had to learn about coding, deal with bosses and coworkers, and survive office politics. You won’t learn that at a university or a bootcamp.&lt;/p&gt;

&lt;p&gt;If you choose this route, don’t try sending CVs like crazy to anywhere and everywhere. &lt;a href=&quot;/2025/02/03/LandingACodingJob/&quot;&gt;Use your network and try to skip hiring lines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don’t stay too long at a corporate job. Your skills will get rusty. Stay long enough to increase your hours of flight time before jumping somewhere else.&lt;/p&gt;

&lt;h2 id=&quot;2-get-good-at-one-skill-and-join-a-high-paying-market&quot;&gt;2. Get good at one skill and join a high-paying market&lt;/h2&gt;

&lt;p&gt;Once you have enough hours of flight time:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Get good at one skill&lt;/strong&gt;: frontend development with React, backend development with .NET, or whatever skill you can put time and effort into learning.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Get good at a foreign language&lt;/strong&gt;: Good enough to work professionally in that language.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Join a software agency and work for high-paying markets like North America or Europe&lt;/strong&gt;: You can take advantage of currency strength between countries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Based on your country of residence, you can make good money with this route compared to local markets.&lt;/p&gt;

&lt;h2 id=&quot;3-going-on-your-own&quot;&gt;3. Going on your own&lt;/h2&gt;

&lt;p&gt;Points #1 and #2 were like getting someone to feed you. This is like hunting on your own.&lt;/p&gt;

&lt;p&gt;Find small businesses in your city and see how you can help them with automation. You’d be surprised how many small businesses and mom-and-pop shops run their daily operations with pen-and-paper or Excel.&lt;/p&gt;

&lt;p&gt;If you choose this route, you can’t tell clients “I write HTML, CSS, and JavaScript. I care about Clean Code and I’m not a coder, but a craftsman.”&lt;/p&gt;

&lt;p&gt;Sure, those are nice-to-haves. But that’s the lingo you and I understand. Small businesses don’t care about those things. They care about what you can do for them and their business.&lt;/p&gt;

&lt;p&gt;You have to sell yourself in terms that your clients can understand. “I can help you implement a system that will cut in half the time it takes you to deliver your restaurant orders. Your clients will receive their food while it’s still warm.”&lt;/p&gt;

&lt;p&gt;And don’t even think of writing code from scratch. Go with no-code solutions or easier ways for you to implement. Even installing existing software and training small companies on how to use them.&lt;/p&gt;

&lt;p&gt;I’ve done #1 and #2. And if I had to start again, I’d try something like #3. Good luck!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Land a Coding Job in Post-Pandemic Times—The Untraditional Way</title>
   <link href="https://canro91.github.io/2025/02/03/LandingACodingJob/"/>
   <updated>2025-02-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/03/LandingACodingJob</id>
   <content type="html">&lt;p&gt;Last year, I sent out over a dozen of CVs and cold emails within a month.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;I was laid off&lt;/a&gt;. I went into panic mode. I applied to many companies. Anything with the word “coding” or “software engineering” in the job description. &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;I even applied to a FAANG&lt;/a&gt; though I rejected that idea years ago.&lt;/p&gt;

&lt;p&gt;But I just got radio silence.&lt;/p&gt;

&lt;p&gt;2024 was a tough market. I know because I wasn’t the only one going through layoffs. The entire industry was. I even talked to an ex-coworker and he told me he had sent one hundred applications. I don’t know if he meant literally 100 applications or not. But I wouldn’t be surprised. He only got two or three positive responses.&lt;/p&gt;

&lt;p&gt;Today I found a reflection on dev.to from a junior developer.&lt;/p&gt;

&lt;p&gt;Last year, she finished a coding bootcamp or something and started to look for jobs. Oh boy! It must have been tougher for her. After the bat-soup and 2023, it seems nobody is hiring junior engineers anymore. And she tried &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;the CV route&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CVs are dead. They’re so last century.&lt;/p&gt;

&lt;p&gt;And sending lots of CVs won’t land you a job. Otherwise, someone with a printing business will find tons of jobs just by printing and mailing CVs. Well, sending CVs is a numbers game. Maybe out of 100 applications, you’ll get 2-3% of responses back if you’re lucky. Who knows!&lt;/p&gt;

&lt;p&gt;Instead of CVs, I’d try an indirect approach to look for jobs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Follow on LinkedIn, companies you’d like to work for and people (especially hiring managers) who work there.&lt;/li&gt;
  &lt;li&gt;Start genuine conversations. Ask how working there is and ask for referral programs.&lt;/li&gt;
  &lt;li&gt;If you use the company’s product, &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;show up with ideas&lt;/a&gt; to improve the product. Or the company website or social media presence. Don’t simply send your CV with an “I do anything coding-related for money” attitude. That’s what I did with obviously poor results. And &lt;a href=&quot;/2024/11/06/LookingNeedy/&quot;&gt;don’t appear desperate&lt;/a&gt; either.&lt;/li&gt;
  &lt;li&gt;Go through your network, asking if anyone knows someone working at a place where you could fit. Repeat steps 1 to 3. Or ask for an introduction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I found my first job because I knew someone who knew someone at a company looking for coders. An introduction skipped the hiring line. And after &lt;a href=&quot;/2025/01/14/BeingFired/&quot;&gt;being fired from that job&lt;/a&gt;, I found my next one through my network. I knew someone who knew someone.&lt;/p&gt;

&lt;p&gt;If you’re standing in a hiring line or if your CV is on a pile physically or virtually, you’re already screwed. Don’t wait in hiring lines. Look for indirect ways to open doors and skip the lines.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stand Next to Expensive Things: 10 Lessons from SQL Server Master Brent Ozar</title>
   <link href="https://canro91.github.io/2025/02/02/LessonsFromBrentOzar/"/>
   <updated>2025-02-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/02/LessonsFromBrentOzar</id>
   <content type="html">&lt;p&gt;You don’t know where you will find him next. In a bathtub, a garage, an exotic beach in Mexico, somewhere in Iceland, or his office.&lt;/p&gt;

&lt;p&gt;In every Office Hour, Brent shows a small glimpse of his personality and life.&lt;/p&gt;

&lt;p&gt;I started to follow him for SQL Server advice. He’s maybe &lt;em&gt;the&lt;/em&gt; SQL Server expert. In fact, &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;I took his courses&lt;/a&gt;. But I ended up showing up to his Office Hours for career and consulting advice.&lt;/p&gt;

&lt;p&gt;After watching Brent’s YouTube live sessions, here are 10 lessons I learned from him:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Stand next to expensive things.&lt;/strong&gt; Each SQL Server license costs around ~5k per core. Anything Brent charges for his consulting services is way cheaper than the licensing costs of an average server. Stand next to expensive things to charge premium rates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. When planning your next workshop or course, start by writing the takeaway slide first.&lt;/strong&gt; Then, create the rest of the content around that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. If you ask “when is it time to leave my job?,” then it’s already time.&lt;/strong&gt; If you wait for a layoff to look for a new job, it’s too late. &lt;a href=&quot;/2024/11/17/BestTimeToLookForANewJob/&quot;&gt;The best time is when you don’t need a job&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. If you’re planning to start consulting, start blogging and presenting months and years before to build your reputation.&lt;/strong&gt; And always finish your presentations by saying “If you need more help, feel free to contact me.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Work on the smallest change that brings the most impact.&lt;/strong&gt; This is something I learned from his Mastering courses. Often, we start to turn knobs and push buttons to make our SQL Server faster, without making any noticeable impact. The same applies to work outside the database work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Take a photo of you in your current work environment.&lt;/strong&gt; That’s a future reminder of where you were and what brought you to where you are now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. People follow you by who you are.&lt;/strong&gt; Ok, Brent is opinionated and doesn’t restrict himself from roasting people in his YouTube live sessions. That’s one of the reasons people (and I) follow him. Authenticity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Impostor syndrome never goes away.&lt;/strong&gt; The other day, I asked him how to deal with impostor syndrome. And hearing one of the top SQL Server experts saying he still feels like an impostor when working with clients was relieving. We’re all constantly learning and improving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. You don’t have to be THE expert, you just need to know a bit more than the person you’re helping.&lt;/strong&gt; This goes hand in hand with #8. It takes yeaaars to be the #1 in the world at anything, if you ever reach that point. But you don’t have to wait to be the #1 to start helping others and start making money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Build a brand so powerful that clients line up.&lt;/strong&gt; Chances are that if you google anything related to SQL Server, Brent’s website will pop up. You’ll find him in YouTube, Twitch, and maybe TikTok. At this point, he doesn’t need to chase clients. He has a strong online presence. And it takes years to build one.&lt;/p&gt;

&lt;p&gt;In my hometown, there’s a saying: build a reputation, then go to bed. Brent did that.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To learn how to make your SQL Server go faster with Brent—and support this blog—buy &lt;a href=&quot;https://training.brentozar.com/p/recorded-class-season-pass-fundamentals?affcode=920087_fhe3khrq&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the Fundamentals Bundle here&lt;/a&gt; and the &lt;a href=&quot;https://training.brentozar.com/p/fundamentals-and-mastering-bundle?affcode=920087_fhe3khrq&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Fundamentals + Mastering Bundle here&lt;/a&gt;. I can’t recommend Brent’s courses enough. Learn from a real expert, with real queries.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Learned a Mind-Blowing New Definition of Money. Here It Is</title>
   <link href="https://canro91.github.io/2025/02/01/NewDefinitionOfMoney/"/>
   <updated>2025-02-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/02/01/NewDefinitionOfMoney</id>
   <content type="html">&lt;p&gt;Money means different things for different people.&lt;/p&gt;

&lt;p&gt;For some people, money is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A skill to master&lt;/li&gt;
  &lt;li&gt;A symbol of status&lt;/li&gt;
  &lt;li&gt;A ticket to freedom&lt;/li&gt;
  &lt;li&gt;An exchange of value&lt;/li&gt;
  &lt;li&gt;A game with levels to conquer&lt;/li&gt;
  &lt;li&gt;What we trade in exchange for time&lt;/li&gt;
  &lt;li&gt;A story we’ve been telling as a society&lt;/li&gt;
  &lt;li&gt;A differentiator between classes&lt;/li&gt;
  &lt;li&gt;A multiplier of our values&lt;/li&gt;
  &lt;li&gt;A mindset to practice&lt;/li&gt;
  &lt;li&gt;A social convention&lt;/li&gt;
  &lt;li&gt;The root of all evil&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, these days, watching Devon Eriksen, an engineer turned fiction writer, in &lt;a href=&quot;https://www.youtube.com/watch?v=Oltd80kD3FQ&quot;&gt;an interview on YouTube&lt;/a&gt;, I learned a new definition of money.&lt;/p&gt;

&lt;p&gt;He said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Money is a measure of f*cks given.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want more money you have to make people give a f-ck about you. And if you say you don’t want money, you’re saying you don’t want people to give a f-ck about you.&lt;/p&gt;

&lt;p&gt;Devon’s analogy dates back to our time as hunters and gatherers, when there was no concept of money as we know it today.&lt;/p&gt;

&lt;p&gt;In those days, if you were a hunter, you cared (or gave a f-ck to follow the analogy) about the guy who polished rocks to make spears. And you two exchanged money in the form of products and value. Spears for meat or anything else. Caring meant exchange and exchange meant money.&lt;/p&gt;

&lt;p&gt;Today we don’t trade goods or services, we have pieces of paper and figures on a screen. Create something people care about and let money roll in.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>There&apos;s No Perfect Time to Start Anything—Start Now!</title>
   <link href="https://canro91.github.io/2025/01/31/NoPerfectTime/"/>
   <updated>2025-01-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/31/NoPerfectTime</id>
   <content type="html">&lt;p&gt;Is there ever a perfect time to start anything?&lt;/p&gt;

&lt;p&gt;Of course, no. “Perfect time” or “waiting to be ready” means never. And we will never be “ready” for anything in life.&lt;/p&gt;

&lt;p&gt;Back in high school, we had two classmates who we all knew had a crush on each other.&lt;/p&gt;

&lt;p&gt;And, as teenagers figuring out the new world of relationships, we all encouraged our classmate to ask his crush out. He always said: “Next month, I’m asking her out.” And then, the next month, he said: “Next month, I’m asking her out”. Then “next month…” and “next month…” He kept waiting for the perfect time.&lt;/p&gt;

&lt;p&gt;“Next month” became never.&lt;/p&gt;

&lt;p&gt;And it works the same for any creative project.&lt;/p&gt;

&lt;p&gt;There’s no perfect time to start. You will never be ready. “Next month” means never.&lt;/p&gt;

&lt;p&gt;So &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;start that blog&lt;/a&gt;, start that YouTube channel, start taking those pictures, start that poetry book. You don’t need to be an expert to start. You don’t need to be famous. You don’t need to wait for the perfect time. You just need to start.&lt;/p&gt;

&lt;p&gt;So why wait? Start today. Keep showing up and adapt as you go. The best time was five years ago. The next best time to start is right now.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dear Junior Coders: Stop Chasing Shiny Objects</title>
   <link href="https://canro91.github.io/2025/01/30/ChasingShinyObjects/"/>
   <updated>2025-01-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/30/ChasingShinyObjects</id>
   <content type="html">&lt;p&gt;“Focus on learning one thing.”&lt;/p&gt;

&lt;p&gt;A coworker told me that every time he got to my desk. At that time, he was the IT/network guy. Years before that, he was a certified Java engineer or something.&lt;/p&gt;

&lt;p&gt;I was new at this coding thing. I was trying to learn about everything at once. It was back in 2010ish. I was reading &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;The Clean Code&lt;/a&gt;, learning Python, using C# at work (coming from Java), and watching PHP presentations in my lunch break.&lt;/p&gt;

&lt;p&gt;Now you see why my coworker told me to focus.&lt;/p&gt;

&lt;h2 id=&quot;dont-chase-shiny-objects-go-deep-into-fewerthings&quot;&gt;Don’t chase shiny objects, go deep into fewer things&lt;/h2&gt;

&lt;p&gt;Instead of chasing new and shiny objects (like tools, libraries, and frameworks), juniors (and we all) are better off going deep into fewer tools and concepts.&lt;/p&gt;

&lt;p&gt;Me 10+ years ago? “Oh there’s a new framework. &lt;a href=&quot;/2024/07/08/CSharpInconsistencies/&quot;&gt;A new C# version&lt;/a&gt;. A new CI/CD tool. Hey, what’s that new Hangfire thing over there?” Arrggg!&lt;/p&gt;

&lt;p&gt;Frameworks and libraries come and go.&lt;/p&gt;

&lt;p&gt;Today it’s React with Typescript. Before that, it was Bootstrap with Ember or Knockout.js. Before that, it was ASP.NET WebForms. Before that, it was Perl scripts or something. I wasn’t around coding at that time.&lt;/p&gt;

&lt;p&gt;And who knows what &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;AI will bring to the table&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But chances are we’ll be working on a C-type language, still using text files, and writing SQL. That hasn’t changed in ~50 years. And it will remain the same. I wouldn’t bet all my money though.&lt;/p&gt;

&lt;p&gt;If you’re starting your coding journey, master the topics that have passed the test of time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SQL&lt;/li&gt;
  &lt;li&gt;HTTP&lt;/li&gt;
  &lt;li&gt;C/C++&lt;/li&gt;
  &lt;li&gt;Data structures&lt;/li&gt;
  &lt;li&gt;Design patterns&lt;/li&gt;
  &lt;li&gt;Vanilla JavaScript&lt;/li&gt;
  &lt;li&gt;Clean code principles&lt;/li&gt;
  &lt;li&gt;Debugging and testing&lt;/li&gt;
  &lt;li&gt;Linux and operating systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Not all of them at once, of course.)&lt;/p&gt;

&lt;p&gt;I don’t know what other subject to add to that list now. But you get the point.&lt;/p&gt;

&lt;p&gt;And more importantly than spitting out code, master your soft skills: negotiation and persuasion. Coding is more about collaboration than cracking symbols on a file.&lt;/p&gt;

&lt;p&gt;It took me quite a while to learn that lesson. And that’s why I wrote &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; It’s the roadmap I wish I had to go from junior to senior.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=stop-chasing-shiny-objects&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;.&lt;/em&gt; Because coding is more than chasing trends. It’s about building skills that last.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Schedule Rest the Same Way You Schedule Important Meetings</title>
   <link href="https://canro91.github.io/2025/01/29/ScheduleRest/"/>
   <updated>2025-01-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/29/ScheduleRest</id>
   <content type="html">&lt;p&gt;For the first time after over 10 years of non-stop work, in 2024 I suddenly found myself with lots of free time.&lt;/p&gt;

&lt;p&gt;Thanks to the &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;layoff in the tech industry&lt;/a&gt; and to &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;fully recover from burnout&lt;/a&gt;, I decided to take a mini-retirement.&lt;/p&gt;

&lt;p&gt;All of a sudden, I had 8 extra hours free on my day.&lt;/p&gt;

&lt;p&gt;No more meetings, 1-on-1s, estimation sessions, or any other meeting that could be an email. I went nuts with that free time. Reading books, writing on my blog, reposting on dev.to and Medium, recording video courses, reviving my LinkedIn account, doing some freelance coding here and there.&lt;/p&gt;

&lt;p&gt;I went to bed more tired than a full day of work. It felt so good. I was the master and owner of my calendar for the first time in years. Monday mornings didn’t feel the same.&lt;/p&gt;

&lt;p&gt;But I realized I was chasing my ambitions.&lt;/p&gt;

&lt;p&gt;The time that was supposed to be for rest and reconnecting with myself was as busy as the days when I had a full-time job.&lt;/p&gt;

&lt;p&gt;Reflecting on this, I realized I need a change. I decided to start a new habit: &lt;strong&gt;Scheduling my rest time the same way I schedule important appointments on my calendar&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One day a week for one hour, I have a guilt-free time slot. I’m free to spend that hour doing whatever I want. The only rule is: zero work. Most of the time, I take a walk or read or watch a TV show.&lt;/p&gt;

&lt;p&gt;We often are trapped in the productivity wheel that we forget rest is as important as active work, if not more important.&lt;/p&gt;

&lt;p&gt;Rest time is the trigger for aha moments and bursts of inspiration. Often after taking a walk during my guilt-free hour, I have to rush to my computer to write something that came up while walking.&lt;/p&gt;

&lt;p&gt;Remember to schedule your rest time too. You deserve it. And your mind and body need it.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>10 More Practical Ideas to Instantly Double Your Blog Post Count</title>
   <link href="https://canro91.github.io/2025/01/28/DoubleYourPostCount/"/>
   <updated>2025-01-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/28/DoubleYourPostCount</id>
   <content type="html">&lt;p&gt;Too much of anything is bad. Except for writing.&lt;/p&gt;

&lt;p&gt;Here are 10 more ideas to write or blog more—I say “10 more” because I’ve already shared &lt;a href=&quot;/2024/11/13/BlogMore/&quot;&gt;2 ideas to write more&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1. Write shorter posts.&lt;/strong&gt; Instead of writing a 1,000 or 2,000-word post, write multiple 200 or 500-word posts and make them a series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. If you write a listicle (“10 tips to …” or “10 lessons I learned from …”), expand some items in separate posts.&lt;/strong&gt; I used this technique when I wrote about &lt;a href=&quot;/2025/01/19/ChooseYourselfGuideToWealth/&quot;&gt;Choose Yourself Guide to Wealth&lt;/a&gt; and &lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;my alternative to to-do lists&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Write tangents in a separate post.&lt;/strong&gt; When you’re writing and notice you’re going on a tangent, don’t delete it. Use it as a starting point for another post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Turn your “cuts” into posts.&lt;/strong&gt; When you’re editing or proofreading, don’t throw away the sentences or paragraphs that didn’t make it into the final piece. They’re ideas for separate posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#5. Update old posts in new posts.&lt;/strong&gt; If the subject of a post has changed significantly since the first time, write an updated version and add an “update” disclaimer to the original one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#6. Take notes publicly&lt;/strong&gt; after finishing  a book or listening to a podcast episode. I do this all the time. Just look at &lt;a href=&quot;/tags/books/&quot;&gt;my books tag&lt;/a&gt;. You could summarize it, react to it, or collect your favorite quotes and tell stories around them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#7. Answer private questions in public.&lt;/strong&gt; Every time I get a message on my contact page or a private text message with an interesting question, I expand my response into a post. Of course, editing out anything that can’t be shared in public. That’s how &lt;a href=&quot;/2024/11/09/LimitedKeystrokes/&quot;&gt;I preserve my keystrokes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#8. Answer Reddit, Hacker News, or Quora questions on your own post.&lt;/strong&gt; When you find a question that you can answer, write your own “answer” post. I did this when I wrote about &lt;a href=&quot;/2024/12/12/LifeChangingPurchases/&quot;&gt;my life-changing purchases since 2020&lt;/a&gt;. That was a question I found on Hacker News.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#9. Turn old comments into posts.&lt;/strong&gt; Review the comments you’ve left on social media and forums and expand your comments into posts. Especially use those comments that got good engagement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#10. Steal topics from others.&lt;/strong&gt; This is a variation of #8. When you’re reading something and you realize you could have written that post, stop reading and write your own. Steal the topic, but not the content. I did this when I wrote about the lessons I learned from &lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;The Almanack of Naval Ravikant&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There’s no bad consequence of writing more. &lt;a href=&quot;/2025/03/17/VolumeWins/&quot;&gt;Volume always wins&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My Blogging Strategy in Times of Dead Blogging</title>
   <link href="https://canro91.github.io/2025/01/27/TimesOfDeadBlogging/"/>
   <updated>2025-01-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/27/TimesOfDeadBlogging</id>
   <content type="html">&lt;p&gt;Blogging is dead. At least, that’s what almost everybody claims.&lt;/p&gt;

&lt;p&gt;And it’s been dead for years. But in spite of that belief, we still find (random and famous) people writing on their blogs: Seth Godin, Herbert Lui, and me (shameless plug).&lt;/p&gt;

&lt;h2 id=&quot;with-blogs-we-have-control-but-lose-distribution&quot;&gt;With blogs, we have control but lose distribution&lt;/h2&gt;

&lt;p&gt;In our blogs, we aren’t at the mercy of third-party platforms and their algorithms.&lt;/p&gt;

&lt;p&gt;A blog is a place you own. You can redesign it and write anything you want. It’s like your small kingdom. And I like to think of my blog as &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;my time capsule&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But blogs don’t have a distribution mechanism.&lt;/p&gt;

&lt;p&gt;Apart from search engines and cross-posting, there’s no “feed” for blogs. Well, there’s RSS, but you still have to find a blog in the first place to add it to your RSS reader.&lt;/p&gt;

&lt;p&gt;Distribution is where social media truly shines. Unlike blogs, social media shine at putting content in front of readers. This means we can see traction way faster on social media compared to a blog sitting behind search engines.&lt;/p&gt;

&lt;p&gt;But we don’t control our social media accounts.&lt;/p&gt;

&lt;p&gt;A social media platform can change its rules at any time. Just take a look at Medium, everybody is complaining about getting lower payouts after a change in the algorithm.&lt;/p&gt;

&lt;p&gt;Getting banned on social media is possible if you don’t behave.&lt;/p&gt;

&lt;h2 id=&quot;we-need-a-combination-of-both-social-media-for-short-form-content-and-blogs-and-newsletters-for-long-form-content&quot;&gt;We need a combination of both: social media for short-form content and blogs (and newsletters) for long-form content&lt;/h2&gt;

&lt;p&gt;Since 2024, I started to use my blog as my main hub for my content.&lt;/p&gt;

&lt;p&gt;Ultimately, all my content ends up here on my blog in some shape or form. A single blog post ends up in multiple shorter posts for social media. And a short social media post can evolve into larger posts or sections. That’s my content wheel.&lt;/p&gt;

&lt;p&gt;And I’m not only writing SEO-optimized 1,000-word posts about programming anymore.&lt;/p&gt;

&lt;p&gt;Apart from coding and software engineering, I’m writing about my other interests. We’re not single-interest creatures. I’m not. &lt;a href=&quot;/2024/12/16/FindYourPassion/&quot;&gt;I’m a multipotentialite&lt;/a&gt;. In fact, I created &lt;a href=&quot;/tags/misc/&quot;&gt;a new tag&lt;/a&gt; as a catch-all for my “other” content.&lt;/p&gt;

&lt;p&gt;My new rule is that if &lt;a href=&quot;/2024/11/13/BlogMore/&quot;&gt;an idea is larger than a Tweet, it can be a post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I cross-post on other platforms, I invite readers to &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot;&gt;my newsletter&lt;/a&gt;. And on every email I send, I promote two posts I write here and the products I’m building.&lt;/p&gt;

&lt;p&gt;But if I had to start all over again today, I wouldn’t start with a blog. I’d go with a social media account and a newsletter.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Content Ideas Are Already There, You Just Need to Search for Them</title>
   <link href="https://canro91.github.io/2025/01/26/ContentIsEverywhere/"/>
   <updated>2025-01-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/26/ContentIsEverywhere</id>
   <content type="html">&lt;p&gt;“Money has already been made, we just have to get out and search for it.”&lt;/p&gt;

&lt;p&gt;That’s a common phrase in my hometown. It means money rewards action. And like I learned from hearing and watching James Altucher, nobody wakes up and thinks about giving money to any of us. We have to search for it.&lt;/p&gt;

&lt;p&gt;That applies to content ideas too. Replace “money” with “content” and you’ll get the same meaning.&lt;/p&gt;

&lt;p&gt;When we wear our writer (or creative) glasses, there’s content everywhere:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Books&lt;/li&gt;
  &lt;li&gt;Questions&lt;/li&gt;
  &lt;li&gt;Conversations&lt;/li&gt;
  &lt;li&gt;Emails you reply to&lt;/li&gt;
  &lt;li&gt;Projects you’re working on&lt;/li&gt;
  &lt;li&gt;Comments you leave on social media&lt;/li&gt;
  &lt;li&gt;Other people’s posts&lt;/li&gt;
  &lt;li&gt;Situations at work&lt;/li&gt;
  &lt;li&gt;TV shows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everywhere.&lt;/p&gt;

&lt;p&gt;For example, the other day I realized the same coworker had asked the same question about investing on a WhatsApp group. &lt;a href=&quot;/2024/11/07/CompaniesToBuy/&quot;&gt;Then I wrote about it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another coworker asked me how to start writing on LinkedIn. &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;Then I wrote about it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another day I was reading a book review. And I realized I had read the same book. I stopped reading and &lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;wrote my own review&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another day I found a post I didn’t agree with. &lt;a href=&quot;/2024/11/14/NoNulIsAGoodIdea/&quot;&gt;Then I wrote a reaction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I felt guilty for binge-watching my favorite TV shows until I started to write about them. Now watching Netflix and TV shows is an excuse to write more content. Content is everywhere.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Missed the Last 10 Years of C#? Read This (Quick) Guide</title>
   <link href="https://canro91.github.io/2025/01/25/DotNetCatchUpGuide/"/>
   <updated>2025-01-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/25/DotNetCatchUpGuide</id>
   <content type="html">&lt;p&gt;Here’s an opinionated catch-up guide if you missed the last decade of the C#/.NET/Microsoft evolution.&lt;/p&gt;

&lt;p&gt;#1. &lt;strong&gt;C# isn’t JAVA anymore.&lt;/strong&gt; That joke doesn’t work anymore. The two languages have taken completely different paths. They don’t even look the same.&lt;/p&gt;

&lt;p&gt;#2. &lt;strong&gt;There’s a new runtime: .NET.&lt;/strong&gt; It’s multiplatform and open-sourced. We have the classic one, we call it: .NET Framework, which you probably already know. And the new one was called “.NET Core.” But now, it’s simply “.NET” Yes, naming is hard.&lt;/p&gt;

&lt;p&gt;#3. Speaking of the language, &lt;strong&gt;the main language features are still there.&lt;/strong&gt; C# is an imperative side-effect-heavy language. But with every release, it’s adopting more features from functional languages, like lambda expressions, pattern matching, and records. (See #7)&lt;/p&gt;

&lt;p&gt;#4. There’s excellent &lt;strong&gt;support for asynchronous methods&lt;/strong&gt; with two keywords: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;#5. &lt;strong&gt;There’s a whole lot of new small language features.&lt;/strong&gt; Apart from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; (from C# 5.0) we haven’t had a release with a single major feature. Most of the new features are syntactic sugar for existing features.&lt;/p&gt;

&lt;p&gt;#6. &lt;strong&gt;There’s a new feature you shouldn’t miss&lt;/strong&gt;: &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;nullable references&lt;/a&gt;. It should have been called: “non-nullable references” instead. Do you remember nullable primitive types? Like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int?&lt;/code&gt;? We allow a type to contain null by appending a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt; when declaring it. We can do the same for classes. So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie? m&lt;/code&gt; can be null, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie m&lt;/code&gt; can’t. And the compiler warns us when we’re trying to use a reference that might be null. Awesome!&lt;/p&gt;

&lt;p&gt;#7. I compiled a list of &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;C# features we should know about&lt;/a&gt;. Those are the features I like and have used the most.&lt;/p&gt;

&lt;p&gt;#8. &lt;strong&gt;A “Hello, world” is now literally one line of code.&lt;/strong&gt; We don’t need to declare a class and a Main method for console applications. Just write your code directly in a file like in a scripting language.&lt;/p&gt;

&lt;p&gt;#9. I stopped expanding that list with the most recent features. &lt;strong&gt;C# is getting bloated&lt;/strong&gt; with more features that are &lt;a href=&quot;/2024/07/08/CSharpInconsistencies/&quot;&gt;making the language less consistent&lt;/a&gt;. I don’t like that.&lt;/p&gt;

&lt;p&gt;#10. The same way we have a new runtime, &lt;strong&gt;we have a new web framework: ASP.NET Core.&lt;/strong&gt; It was a full rewrite of the classic ASP.NET. There’s no Global.asax, web.config, or files listed on csproj files. If you knew and used the old ASP.NET, I wrote a &lt;a href=&quot;/2020/03/23/GuideToNetCore/&quot;&gt;guide with the difference between the two&lt;/a&gt;. With .NET, we have way better tooling like a &lt;a href=&quot;/2022/12/15/CreateProjectStructureWithDotNetCli/&quot;&gt;command line interface to create projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;#11. Well, there’s Xamarin, MAUI, Blazor… But unless you’re planning to do front-end work, you don’t need to worry about them. Microsoft is still trying to find, create, and establish a golden hammer for the front-end side.&lt;/p&gt;

&lt;p&gt;Sure, I’m missing a lot of other things. But you’re safe catching up on those.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How To Ignore Unmapped Fields in the Destination Type With AutoMapper</title>
   <link href="https://canro91.github.io/2025/01/24/IgnoringPropertiesAutoMapper/"/>
   <updated>2025-01-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/24/IgnoringPropertiesAutoMapper</id>
   <content type="html">&lt;p&gt;Another day working with AutoMapper, another day with an edge case.&lt;/p&gt;

&lt;p&gt;Let’s say we have class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateMovieRequest&lt;/code&gt; with three properties: name, release year, and another property I don’t want to map, for some reason. And a destination class, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt;, with more properties apart from those three and names using a prefix.&lt;/p&gt;

&lt;p&gt;Simply trying to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateMap&amp;lt;CreateMovieRequest, Movie&amp;gt;()&lt;/code&gt; won’t work. AutoMapper will warn you about unmapped properties in the destination type. “Unmapped members were found. Review the types and members below.”&lt;/p&gt;

&lt;p&gt;Here’s how to map two classes ignoring the unmapped properties in the destination type and with prefixes:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AutoMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TestProject1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IHateYouWilWheaton&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateMovieRequest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDontWantThisOneMapped&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// These two aren&apos;t mapped from the source&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DurationInMinutes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// These two have the &apos;Movie&apos; prefix&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IgnoringNotMappedProps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapperConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cfg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Let&apos;s keep it here, shall we?&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RecognizeDestinationPrefixes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Movie&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// To avoid explicitly mapping the fields because of the prefix&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// cfg.CreateMap&amp;lt;CreateMovieRequest, Movie&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// AutoMapper.AutoMapperConfigurationException: &lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Unmapped members were found. Review the types and members below.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Unmapped properties:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Id&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// MovieName&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// DurationInMinutes&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// After:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateMovieRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MemberList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//                                   ^^^^^&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// To validate unmapped properties on the source type&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Possible options: Source, Destination, and None			&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ForSourceMember&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDontWantThisOneMapped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoNotValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Since I don&apos;t want this one mapped for some reason&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AssertConfigurationIsValid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateMovieRequest&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Space Odyssey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1968&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;IDontWantThisOneMapped&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Ok, if you say so&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The trick for prefixes is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RecognizeDestinationPrefixes()&lt;/code&gt; and the one for warnings is passing any of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Destination&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt; as a parameter to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Map()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And to ignore an “incoming” property, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForSourceMember()&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoNotValidate()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>You Don&apos;t Need a Published Book to Call Yourself a Writer</title>
   <link href="https://canro91.github.io/2025/01/23/CallingYourselfAWriter/"/>
   <updated>2025-01-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/23/CallingYourselfAWriter</id>
   <content type="html">&lt;p&gt;Is it a best-selling book? A Pulitzer-winning novel? A large newsletter? More than 1,000 articles published anywhere online?&lt;/p&gt;

&lt;p&gt;When can we call ourselves “writers”?&lt;/p&gt;

&lt;p&gt;I have to confess I’ve been reluctant to use “digital writer” or “technical writer” or “writer” as a title anywhere online, even though I’ve been writing in my small corner of the Internet since 2018.&lt;/p&gt;

&lt;p&gt;But recently, &lt;a href=&quot;https://www.youtube.com/watch?v=Oltd80kD3FQ&quot;&gt;watching this interview with Devon Eriksen on YouTube&lt;/a&gt;, I changed my mind about using the title “writer.” He says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you’ve written and you’ve gotten paid for it. You’re a writer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After hearing that, I wasn’t afraid to change my tagline on social media and here on my blog.&lt;/p&gt;

&lt;p&gt;Definitely, someone has asked me to write something and paid me for that. That was by pure accident or luck, thanks to sharing the tutorials I write here on my blog. That’s how I made my first internet money.&lt;/p&gt;

&lt;p&gt;You don’t need to publish a book. Well, publishing a book isn’t that hard these days. James Altucher has a strategy to &lt;a href=&quot;/2024/12/27/WritingABook/&quot;&gt;publish a book in 30 days&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You don’t need to write and publish a book to call yourself a writer. Find one person that will pay to write and start calling yourself a “writer.” I’m a writer. And you?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Configure Default Values for Nullable Columns With Default Constraints in EntityFramework Core</title>
   <link href="https://canro91.github.io/2025/01/22/EFAndDefaultConstraints/"/>
   <updated>2025-01-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/22/EFAndDefaultConstraints</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: For nullable columns with default constraints, you have to tell EntityFramework the default value of the mapping property via C#. Otherwise, when you create a new record, it will have NULL instead of the default value in the database. You’re welcome! Bye!&lt;/p&gt;

&lt;h2 id=&quot;lets-create-a-dummy-table-with-one-nullable-column-but-with-a-defualt-constraint&quot;&gt;Let’s create a dummy table with one nullable column but with a defualt constraint&lt;/h2&gt;

&lt;p&gt;Let’s create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movies&lt;/code&gt; database with one table called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movies&lt;/code&gt;, with an Id, a name, and a director name as optional, but with a default value. Like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DirectorName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;ThisIsADefaultValue&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;lets-create-a-new-record-without-the-nullable-column-and-read-it-back&quot;&gt;Let’s create a new record (without the nullable column) and read it back&lt;/h2&gt;

&lt;p&gt;Now to prove a point, let’s use EntityFramework Core to insert a new movie without passing a director name and read it back.&lt;/p&gt;

&lt;p&gt;What will be the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DirectorName&lt;/code&gt; once we read it back? Null? The value inside the default constraint? Make your bets!&lt;/p&gt;

&lt;p&gt;Here we go,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore.Metadata.Builders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TestProject1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EntityFrameworkAndDefaults&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestInsertAndReadMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Server=(localdb)\\MSSQLLocalDB;Database=Movies;Trusted_Connection=True;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbContextOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContextOptionsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseSqlServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Inception&quot;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// No director name here...&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Inception&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ThisIsADefaultValue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     ^^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     Assert.AreEqual failed. Expected:&amp;lt;ThisIsADefaultValue&amp;gt;. Actual:&amp;lt;(null)&amp;gt;. &lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Whaaaaat!&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoviesContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectorName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, to my surprise that test fails. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movie.DirectorName&lt;/code&gt; isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;ThisIsADefaultValue&quot;&lt;/code&gt;. It’s null.&lt;/p&gt;

&lt;p&gt;I was expecting to see the value from the default constraint in the database. But, no. Wah, wah, wah, Wahhhhhhh…&lt;/p&gt;

&lt;h2 id=&quot;here-you-have-it-entityframework-can-i-get-my-default-value-now&quot;&gt;Here you have it EntityFramework. Can I get my default value now?&lt;/h2&gt;

&lt;p&gt;We have to tell EntityFramework Core the default value of our column, like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoviesContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnModelCreating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modelBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// For simplicity, let keep it here...&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;modelBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;entity&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasDefaultValueSql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ThisIsADefaultValue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Here you have it EntityFramework Core&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Can I get my default value now?&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This behavior only adds up to my love and hate relationship with EntityFramework Core.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>AI Is The Future And We, Coders, Have To Adapt</title>
   <link href="https://canro91.github.io/2025/01/21/AIAdaptation/"/>
   <updated>2025-01-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/21/AIAdaptation</id>
   <content type="html">&lt;p&gt;AI is here to stay. We can’t deny that.&lt;/p&gt;

&lt;p&gt;One day, will AI become self-conscious and try to exterminate us all? Who knows. That’s another discussion.&lt;/p&gt;

&lt;h2 id=&quot;but-we-as-coders-have-to-adapt-as-we-always-have-and-adopt-ai&quot;&gt;But we, as coders, have to adapt, as we always have, and adopt AI.&lt;/h2&gt;

&lt;p&gt;Today, I had a conversation with a group of ex-coworkers.&lt;/p&gt;

&lt;p&gt;One of the members shared he was asked to finish almost an entire working app as part of a &lt;a href=&quot;/2021/11/22/CodingChallengeTips/&quot;&gt;take-home coding challenge&lt;/a&gt; for an interview process at a startup. He was given only 5 hours. We all agreed that 5 hours wasn’t enough.&lt;/p&gt;

&lt;p&gt;Someone else said he should have used AI. But he refused to use AI.&lt;/p&gt;

&lt;p&gt;Maybe the intention behind that take-home challenge was to use AI and come up with a decent working prototype quickly. Who knows? Companies aren’t that clear with their interview process. And hiring is broken.&lt;/p&gt;

&lt;h2 id=&quot;but-we-cant-refuse-to-use-ai&quot;&gt;But we can’t refuse to use AI.&lt;/h2&gt;

&lt;p&gt;AI is here to make us more efficient.&lt;/p&gt;

&lt;p&gt;What used to take days, now it only takes hours or minutes. By pressing a button, we compile thousands of source files in just seconds. Refusing to use AI is like refusing to use calculators or spreadsheets for accounting. It’s like refusing to travel by plane or drive a car.&lt;/p&gt;

&lt;p&gt;These days when efficiency and productivity are rewarded, AI is a game-changer.&lt;/p&gt;

&lt;p&gt;We can’t pretend to keep using punch cards and computers that fill an entire room. AI is the future. And we have to adapt. &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;Coding in 2034 will change&lt;/a&gt;. But, one thing is certain: we can’t use AI to replace our thinking. We should be the pilots, and AI as our copilot.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Writing Isn&apos;t Something That Only Writers Do</title>
   <link href="https://canro91.github.io/2025/01/20/WritingToLearn/"/>
   <updated>2025-01-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/20/WritingToLearn</id>
   <content type="html">&lt;p&gt;What’s the first thing that comes to your mind when you hear “writer”?&lt;/p&gt;

&lt;p&gt;Probably it’s a starving artist isolated in a cabin, in the middle of nowhere, typing the next award-winning best-selling novel on an old typewriter, holding a cigar.&lt;/p&gt;

&lt;p&gt;The truth is you don’t have to be a “writer” to write.&lt;/p&gt;

&lt;p&gt;That’s the main lesson from “Writing to Learn” by William Zinsser. Here are five lessons I learned from that book—in five quotes:&lt;/p&gt;

&lt;h2 id=&quot;1-contrary-to-general-belief-writing-isnt-something-that-only-writers-do-writing-is-a-basic-skill-for-getting-through-life&quot;&gt;1. “Contrary to general belief, writing isn’t something that only “writers” do; writing is a basic skill for getting through life.”&lt;/h2&gt;

&lt;p&gt;A CV, a cover letter, an email for a client, a request for time off, and a resignation email. What do they all have in common? Writing!&lt;/p&gt;

&lt;p&gt;And that’s only thinking of an employee in a full-time job. But writing is everywhere else.&lt;/p&gt;

&lt;h2 id=&quot;2-writing-organizes-and-clarifies-our-thoughts&quot;&gt;2. “Writing organizes and clarifies our thoughts”&lt;/h2&gt;

&lt;p&gt;If you want to check if you know a subject well enough, try to explain it and even better, write about it.&lt;/p&gt;

&lt;p&gt;When you write you need a message, a chain of thoughts, and a reader persona. You need the right words and the right amount of them. The act of writing solidifies your understanding and shows you your gaps.&lt;/p&gt;

&lt;p&gt;Writing is thinking.&lt;/p&gt;

&lt;h2 id=&quot;3-writing-is-learned-mainly-by-imitation&quot;&gt;3. “Writing is learned mainly by imitation”&lt;/h2&gt;

&lt;p&gt;Writers are readers too.&lt;/p&gt;

&lt;p&gt;And noticing your behavior as a reader is your cheat code to better writing. Notice your favorite passages from the books you read, the posts you read until the end, the social media posts that make you stop scrolling. How did they grab your attention? What title did they use? Why did they make you read?&lt;/p&gt;

&lt;p&gt;If you need a writing mentor, hand-copy your favorite writer and find patterns in their writing.&lt;/p&gt;

&lt;h2 id=&quot;4-the-hard-part-isnt-the-writing-the-hard-part-is-the-thinking&quot;&gt;4. “The hard part isn’t the writing, the hard part is the thinking”&lt;/h2&gt;

&lt;p&gt;There’s writing to share information and writing to discover what we want to say.&lt;/p&gt;

&lt;p&gt;When you write to share information, you need to ask yourself, “what do I want to say?” And when you write to discover what to say, you’re free to leave questions without answers and to build up from an answer you don’t have yet.&lt;/p&gt;

&lt;p&gt;Either type of writing makes you think clearly.&lt;/p&gt;

&lt;h2 id=&quot;5-non-fiction-writing-should-always-have-a-point-it-should-leave-the-reader-with-a-set-of-facts-or-an-idea-or-a-point-of-view-that-he-didnt-have-when-he-started-reading&quot;&gt;5. “Non-fiction writing should always have a point: it should leave the reader with a set of facts, or an idea, or a point of view, that he didn’t have when he started reading”&lt;/h2&gt;

&lt;p&gt;Can this benefit one person?&lt;/p&gt;

&lt;p&gt;That’s the best guideline to decide what to write. If it can benefit one person, you can write lessons, stories, rants, mistakes, questions, and predictions… Anything!&lt;/p&gt;

&lt;p&gt;If it can benefit one person, you’re safe to write. And that one person can be your past self.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;You don’t need to be an expert to write. Who’s an expert anyway? You don’t need to be famous either. Share what you have learned. Share what you’re learning. Share what inspires you. You don’t need to be a writer for that. Write! Probably the next time you’re Googling something you’ll find your own writing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This Book Changed My Mind About To-Do Lists and Business</title>
   <link href="https://canro91.github.io/2025/01/19/ChooseYourselfGuideToWealth/"/>
   <updated>2025-01-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/19/ChooseYourselfGuideToWealth</id>
   <content type="html">&lt;p&gt;Ideas are the new currency of the 21st century.&lt;/p&gt;

&lt;p&gt;If you’re familiar with &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher’s work&lt;/a&gt;, you know about becoming an Idea Machine by &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;writing 10 bad ideas a day&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But “Choose Yourself Guide to Wealth” takes the Idea Machine concept further, covering how to turn your ideas into reality.&lt;/p&gt;

&lt;p&gt;Here are four lessons I learned from reading “Choose Yourself Guide to Wealth”:&lt;/p&gt;

&lt;h2 id=&quot;1-dont-do-to-do-lists&quot;&gt;1. Don’t do to-do lists&lt;/h2&gt;

&lt;p&gt;To-do lists are stressful.&lt;/p&gt;

&lt;p&gt;Anything on a to-do list is something you haven’t done and probably never will do. In the meantime, it’s only causing stress.&lt;/p&gt;

&lt;p&gt;I have items in one of my multiple to-do lists from years ago. I haven’t done anything about them yet. Oops!&lt;/p&gt;

&lt;p&gt;Instead of to-do lists and goals, live by themes.&lt;/p&gt;

&lt;p&gt;If you need inspiration to choose your own theme, follow the Daily Practice: Do something for your physical, emotional, spiritual, and mental well-being every day.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2025/01/11/DitchingTodos/&quot;&gt;That’s the only rule I’ve followed&lt;/a&gt; since I read this book.&lt;/p&gt;

&lt;h2 id=&quot;2-become-an-idea-machine&quot;&gt;2. Become an idea machine&lt;/h2&gt;

&lt;p&gt;You don’t need chains to feel like a slave.&lt;/p&gt;

&lt;p&gt;When you work on somebody else’s ideas, you’re a slave. Being an employee means working on somebody else’s ideas. Therefore, being an employee is being an idea slave, even if those ideas are good.&lt;/p&gt;

&lt;p&gt;To be free and successful, come up with ideas every day. If any of your projects fail, come up with a list of ideas to improve it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I think of ideas for other companies who call me, and my ‘business’ is to help them implement those ideas on their own and become massive successes”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;3-be-a-good-salesman-and-negotiator&quot;&gt;3. Be a good salesman and negotiator&lt;/h2&gt;

&lt;p&gt;To make others follow your ideas, you need to be a good salesman and negotiator.&lt;/p&gt;

&lt;p&gt;But &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;sales and negotiation aren’t about closing tactics&lt;/a&gt; or Jedi mind tricks.&lt;/p&gt;

&lt;p&gt;We do business with people we like and trust. So you need to be friends with your buyers. You need to care about the same things they do. You need to sell and negotiate with people you love because a bad client can make you and the people around you miserable.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“One of the best ways of making friends and customers for life is to direct them to a better service or product than yours”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When in doubt, outsource your negotiation to the other side. &lt;em&gt;“Hey, I’m new to this negotiation thing. I need your advice. What would you do if you were in my shoes?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After a good sale, everybody should be happy.&lt;/p&gt;

&lt;h2 id=&quot;4-invest-in-yourself-and-in-experiences&quot;&gt;4. Invest in yourself and in experiences&lt;/h2&gt;

&lt;p&gt;Don’t day trade or invest in mutual funds.&lt;/p&gt;

&lt;p&gt;Have enough cash to sleep well at night. Don’t invest all your money in one place and only invest with people smarter than you.&lt;/p&gt;

&lt;p&gt;Start your own business and invest in experiences and books.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;Ideas can change your life.&lt;/p&gt;

&lt;p&gt;If you don’t know where to start to change it, work on your health. Start by writing ideas to improve your health.&lt;/p&gt;

&lt;p&gt;In 2024, after burnout and a layoff, to get my life back on track I focused on my health by waking up early, working out, writing 200 words, and removing negativity from my life. I remember 2024 as the year when I was the healthiest.&lt;/p&gt;

&lt;p&gt;Adopt your own Daily Practice. Ask yourself, &lt;em&gt;“What I can do to keep my physical, emotional, spiritual, and mental bodies healthy?”&lt;/em&gt; and see how your life changes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stay Away From Controversy—and Other Life Lessons From a 102-Year-Old WW2 Veteran</title>
   <link href="https://canro91.github.io/2025/01/18/WW2Veteran/"/>
   <updated>2025-01-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/18/WW2Veteran</id>
   <content type="html">&lt;p&gt;We all want to know what we know now but ten years ago.&lt;/p&gt;

&lt;p&gt;That’s how &lt;a href=&quot;https://www.reddit.com/r/IAmA/comments/1efxcu5/iama_102_year_old_man_former_chicken_farmer_and/&quot;&gt;this Reddit AMA&lt;/a&gt; felt when I read it. Len, a 102-year-old WW2 veteran, sat with his grandson to answer questions about history, WW2, and life in general. His grandson, who did the writing part, wanted to hear more of Grandpa’s stories.&lt;/p&gt;

&lt;p&gt;These are my favorite answers and quotes.&lt;/p&gt;

&lt;h2 id=&quot;war-is-never-necessary-there-is-no-such-thing-as-a-justified-war&quot;&gt;“War Is Never Necessary. There Is No Such Thing as a Justified War”&lt;/h2&gt;

&lt;p&gt;Len served with the Canadian troops in the liberation of the Netherlands in 1939. Nazis were trying to reach Copenhagen.&lt;/p&gt;

&lt;p&gt;After many years, Len has overcome traumas thanks to his wife and family’s support. But there are memories he can’t erase and, even to this day, he has bad dreams about it. He still remembers his service number – Eight-one-two-four-zero, by the way – and the D-Day is still vivid in his mind. He can’t even watch the landing scene from Saving Private Ryan.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[About D-Day] I just thought “I have a job to do”. Analyze the situation and react. If you stop, dig a hole. You don’t say “I think this is ok” you dig down deeper. Don’t go hunting for souvenirs in a minefield.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Someone in the comments shared the story of her young grandmother during those war times. She and her class learned and sang the Canadian national anthem to the soldiers. She even refused to wear a green hair ribbon to school because the Canadian flag was red.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“We had no idea that the Holocaust was happening. We saw three emaciated people walking in striped pajamas with big smiles on their faces. They had been liberated. That was the first time I learned about the atrocities that had been committed. In some of the media portrayals, this isn’t the case.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“War should NOT happen. Surely there is a lesson. Don’t let it happen again.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;dont-drink-dont-smoke-and-common-sense&quot;&gt;“Don’t Drink, Don’t Smoke, and Common Sense”&lt;/h2&gt;

&lt;p&gt;That was what Len answered when someone asked about what to do to live to such an amazing age.&lt;/p&gt;

&lt;p&gt;His secret for a good life? “Non-smoker, non-drinker, and not necessarily following all the trends. Do your own thing.”&lt;/p&gt;

&lt;p&gt;The higher we climb the mountain of life, the more perspective and the better view we have. From Len’s 102-year-old perspective:&lt;/p&gt;

&lt;p&gt;A perfect day? “Waking up and being alive… A nice visit. A nice conversation with someone. And some tea.”&lt;/p&gt;

&lt;p&gt;To enjoy each day to the fullest? “Surround yourself with people that you love. Take a minute to step back and see all the beauty around you.”&lt;/p&gt;

&lt;p&gt;“People have forgotten the value of life. People focus too much on possessions and not the people and beauty that surrounds them.”&lt;/p&gt;

&lt;h2 id=&quot;during-the-depression-there-was-no-money&quot;&gt;“During the Depression There Was &lt;em&gt;NO&lt;/em&gt; Money”&lt;/h2&gt;

&lt;p&gt;Len has witnessed the adoption of cars, computers, and cell phones, but more shocking: he lived through the Great Depression.&lt;/p&gt;

&lt;p&gt;Apart from pictures and history textbooks, we have no idea of an economic event of such proportions. Just last week (the first week of August 2024, if you’re reading this from the future), the US Stock Market fell between 10% and 20% and we called it “Black Monday” and were afraid of another recession. But here’s what Len shared when he was asked about the Great Depression:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“There’s no way of comparing that. During the Depression, there was NO money. I knew a man who hauled wood by horse to the school. A day’s work got you one dollar. We were okay, we had lots of vegetables and chickens. No use selling a cow because you would only get a dollar so you kept it for meat and milk. There is no comparison.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;are-you-scared-to-die-no-it-happens-to-everyone&quot;&gt;Are You Scared To Die? –”No. It Happens to Everyone”&lt;/h2&gt;

&lt;p&gt;“If I could give myself advice I would say stay away from controversy. If something works stick with it.”&lt;/p&gt;

&lt;p&gt;When asked what young people today should understand: “The value of life. Don’t louse it up.”&lt;/p&gt;

&lt;p&gt;“T.V. is a pain in the neck. Interesting. Informative. Unnecessary.” Oh boy! I wished he had answered when and how he learned this one.&lt;/p&gt;

&lt;p&gt;“Important: integrity and honesty. Unimportant: Money”&lt;/p&gt;

&lt;p&gt;When asked about fearing death: “Not a bit. Lived a good life, what else do you want. No. Nothing wrong with dying.”&lt;/p&gt;

&lt;p&gt;A 40-something asked about what waking up at 102 looks like: “Everything hurts, but I am not suffering.”&lt;/p&gt;

&lt;p&gt;“Take care of your body. You only get one.” “Don’t smoke.”&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This AMA was full of wisdom. But my favorite answer is when someone asked Len what he wanted to do when he grew up: “Happy.”&lt;/p&gt;

&lt;p&gt;I can only imagine how fulfilled he has lived all these years to say at 102 he’s not afraid of death. What a wonderful life!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>3 Lessons I Learned from Watching Two Millionaires Talk About Money</title>
   <link href="https://canro91.github.io/2025/01/17/MillionairesTalking/"/>
   <updated>2025-01-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/17/MillionairesTalking</id>
   <content type="html">&lt;p&gt;Having too much money brings a different set of challenges.&lt;/p&gt;

&lt;p&gt;That’s one of the takeaways from this conversation between two millionaires. It’s one episode of the “Deep Dive with Ali Abdaal.” This time Ali sat down with Andrew Wilkinson.&lt;/p&gt;

&lt;p&gt;Here’s the YouTube episode if you want to watch it:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/VPJYBnmxNMI?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Ali Abdaal started medical school, then became a YouTuber, and since then has run an online education business. But I didn’t know about Andrew Wilkinson. Andrew went from barista to billionaire, coming from a middle-class family.&lt;/p&gt;

&lt;h2 id=&quot;im-as-stressed-as-you-are&quot;&gt;“I’m as Stressed as You Are”&lt;/h2&gt;

&lt;p&gt;For me, hearing Andrew say he was stressed too was one of the most shocking parts of that conversation — right at the start.&lt;/p&gt;

&lt;p&gt;We might believe that money makes life easier. But from that conversation: it just brings a new set of challenges. We have to choose our own money adventure and when to stop.&lt;/p&gt;

&lt;p&gt;This reminds me of a past boss.&lt;/p&gt;

&lt;p&gt;He was a well-known entrepreneur in my city (and maybe in my whole country). He came from a wealthy family and ran more than a couple of successful businesses. But when he tasted the bat soup in 2020, there was no money to add more time to his countdown timer.&lt;/p&gt;

&lt;p&gt;I guess money can’t buy certain things. For everything else, there’s a credit card. I’m stealing that from a TV commercial.&lt;/p&gt;

&lt;h2 id=&quot;be-a-financial-prepper&quot;&gt;“Be a Financial Prepper”&lt;/h2&gt;

&lt;p&gt;Be paranoid. Only the paranoids survive.&lt;/p&gt;

&lt;p&gt;During the conversation, Andrew shared he’s a prepper. Not in the sense of keeping a basement full of weapons, canned food, and gas masks ready for a zombie apocalypse. But in the sense of having multiple income sources.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Be unbreakable financially”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In 2024, I had to internalize that lesson thanks to yet &lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;another round of layoffs in the software industry&lt;/a&gt;. I lost my main income source, a.k.a salary. I had other income sources, but not enough to cover my monthly expenses.&lt;/p&gt;

&lt;p&gt;Robert Kiyosaki is right: “Build your asset column” and “Make your assets pay for your luxuries.”&lt;/p&gt;

&lt;p&gt;I had to truly live it to learn it. Knowledge is only potential power unless put into practice.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The best way to feel rich is to have cashflow”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;be-the-sushi-master-or-the-chipotle-founder&quot;&gt;Be the Sushi Master or the Chipotle Founder&lt;/h2&gt;

&lt;p&gt;Apart from the money lessons, my most important takeaway from this podcast episode is to:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Design your life around your flow state”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could choose to be the best sushi master of the world — worth a Netflix documentary — or the Chipotle founder.&lt;/p&gt;

&lt;p&gt;One has mastered his craft to the point of perfection. Maybe he’s happy having only one restaurant and serving one smallish crowd. The other took a different route and created a reproducible business with thousands of locations, taking himself out of the equation.&lt;/p&gt;

&lt;p&gt;That’s success seen from different perspectives.&lt;/p&gt;

&lt;p&gt;Both of them followed what they enjoyed doing. Each followed a different adventure.&lt;/p&gt;

&lt;p&gt;It sounds like the story of a businessman who ran into a fisherman taking a nap in a hammock under a palm tree. After a long conversation and giving a business plan for free, the businessman realizes that the entrepreneurial journey he was sharing with the fisherman will end with a nap in a hammock under the same palm tree.&lt;/p&gt;

&lt;p&gt;In any case, follow your flow state and delegate things you don’t enjoy.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Towards the end of the conversation, Ali started to ask for advice to expand his own business. It was interesting to hear the business and money insights from a billionaire who came from the middle class. It wasn’t advice for everyone, but I took this last part:&lt;/p&gt;

&lt;p&gt;“Sell something boring to a rich person. Don’t sell a complex product to cheap people.”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Never Run Promotions or Offer Discounts—Plus 4 More Lessons from Spain&apos;s Top Copywriter</title>
   <link href="https://canro91.github.io/2025/01/16/SpainTopCopywriter/"/>
   <updated>2025-01-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/16/SpainTopCopywriter</id>
   <content type="html">&lt;p&gt;He went from unloading cargo trucks to being Spain’s best copywriter.&lt;/p&gt;

&lt;p&gt;So far I’ve only been following English-speaking copywriters. I write landing pages for my coding courses in English. That’s why I hadn’t heard of Isra Bravo.&lt;/p&gt;

&lt;p&gt;In a podcast interview, he shared his best lessons. You can watch the &lt;a href=&quot;https://www.youtube.com/watch?v=2fcW6QGUt6M&quot;&gt;full interview here&lt;/a&gt; in Spanish. I only needed to watch that interview to start following his work.&lt;/p&gt;

&lt;p&gt;You don’t need to learn Spanish to watch that interview. Here are my favorite lessons from that interview:&lt;/p&gt;

&lt;h2 id=&quot;1-never-run-promotions-or-offer-discounts&quot;&gt;1. Never run promotions or offer discounts&lt;/h2&gt;

&lt;p&gt;Instead of trying to make more sales by lowering your prices, improve your product and get better at selling. Don’t do what everybody else is doing.&lt;/p&gt;

&lt;p&gt;Instead of promotions and discounts, offer a launch price to your existing clients. They’re your best clients since they already bought from you.&lt;/p&gt;

&lt;h2 id=&quot;2-good-copy--good-design&quot;&gt;2. Good Copy &amp;gt; Good Design&lt;/h2&gt;

&lt;p&gt;Good copy is more important than an outstanding design.&lt;/p&gt;

&lt;p&gt;Isra’s most popular book, “I Write Because I Like to Make Money,” is an example of bad design with a good copy. Its cover doesn’t have bright colors or fancy fonts. It’s full of text with some bold sentences. It’s the type of book cover we would create in Microsoft Word with 0 design skills. But it reads like a good social media post or a persuasive email.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2025-01-16-SpainTopCopywriter/BookCover.jpg&quot; alt=&quot;I Write Because I Like to Make Money&quot; width=&quot;300px&quot; /&gt;
    &lt;figcaption&gt;I Write Because I Like to Make Money. Via: goodreads.com&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here’s what it says:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I write because I like to make money. And I teach thousands of people, who also like making money, how to do it. My name is Isra Bravo and in 2017 I unloaded trucks and was broke, but not anymore…“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes, that’s on the book cover. And I only translated the first part for you.&lt;/p&gt;

&lt;p&gt;After hearing that and watching the most popular book from the best copywriter in Spain, I started to redesign my website’s homepage to remove visuals and have better copy.&lt;/p&gt;

&lt;p&gt;Don’t get better at design. Get better at copywriting.&lt;/p&gt;

&lt;h2 id=&quot;3-its-easier-to-stand-out-on-saturated-markets&quot;&gt;3. It’s easier to stand out on saturated markets&lt;/h2&gt;

&lt;p&gt;People are afraid of saturated markets.&lt;/p&gt;

&lt;p&gt;YouTube is saturated. LinkedIn is saturated. Instagram is saturated… The whole world is saturated. But Isra teaches that it’s easier to stand out in saturated markets.&lt;/p&gt;

&lt;p&gt;To stand out, you have to do what everybody else isn’t doing and make fun of your competitors in your copy.&lt;/p&gt;

&lt;p&gt;By day, I’m a software engineer. And if you land on anyone’s coding website, everybody is passionate and codes the whole day for passion, except when they’re sleeping, but they dream about code too.&lt;/p&gt;

&lt;p&gt;To stand out in the saturated market of coding, I’m changing my website’s copy to show my failures instead of my achievements—that’s what everybody is doing. Oh, I’m also saying I’m not passionate.&lt;/p&gt;

&lt;p&gt;If you offer any kind of “done for you” services, don’t simply put on your website a bunch of logos of the clients you’ve helped. Everybody does the same. Instead, share a quick story of what you did for them.&lt;/p&gt;

&lt;p&gt;Stand out by doing a good job, delivering it on time, and changing your copy to be different.&lt;/p&gt;

&lt;h2 id=&quot;4-never-offer-a-lower-price-to-new-buyers&quot;&gt;4. Never offer a lower price to new buyers&lt;/h2&gt;

&lt;p&gt;When you offer lower prices to new buyers, you’re betraying your current clients.&lt;/p&gt;

&lt;p&gt;It’s the same feeling when we find out that a recently-hired team member is making a higher salary than us, who have stayed longer at the job and worked harder. Arrggg! We feel cheated and betrayed. Our clients feel the same.&lt;/p&gt;

&lt;p&gt;Never offer a lower price than the one your current clients bought at.&lt;/p&gt;

&lt;h2 id=&quot;5-never-offer-too-much-value-for-free&quot;&gt;5. Never offer too much value for free&lt;/h2&gt;

&lt;p&gt;People don’t value what they get for free the same way they value what they paid for.&lt;/p&gt;

&lt;p&gt;Last year, as a therapy while recovering from burnout, I started to record coding courses. And I heard the advice to give away my course for free in exchange for testimonials. I did it. But I had to nearly chase my friends for a rating and a review. Some of them only logged in and never watched past the first lecture.&lt;/p&gt;

&lt;p&gt;Always leave them wanting more in your copy, but never give away too much for free.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Learn to write and you will never starve again. With words, you can sell anything. Just remember, don’t run promotions or offer discounts.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>There&apos;s Nothing Wrong With Coding Just to Pay the Bills</title>
   <link href="https://canro91.github.io/2025/01/15/CodingJustForMoney/"/>
   <updated>2025-01-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/15/CodingJustForMoney</id>
   <content type="html">&lt;p&gt;I hate seeing “passionate” listed as a requirement in job postings.&lt;/p&gt;

&lt;p&gt;How can we measure passion? Is there a quiz, like those magazine questionnaires? &lt;em&gt;“Find out if you’re a passionate coder in less than 5 minutes with 10 easy-to-answer questions.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The best coders I’ve met at past jobs weren’t what we’d call passionate. By passionate, I mean making open source contributions, speaking at conferences, and writing posts. They were busy enough making money.&lt;/p&gt;

&lt;h2 id=&quot;we-all-start-as-passionate-but-as-time-goes-by-all-that-passion-fades-away&quot;&gt;We all start as “passionate,” but as time goes by all that passion fades away&lt;/h2&gt;

&lt;p&gt;The other day, Miguel, one of my readers, shared a similar experience. Here’s an excerpt of his email:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Personally, I’ve become discouraged in my programming career and no longer aspire to work at one of the most important tech companies. I just want to pay my bills and meet my family’s needs.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are in a similar situation, you’re not alone.&lt;/p&gt;

&lt;p&gt;At some point in our careers, we all feel the same way. I know I have.&lt;/p&gt;

&lt;p&gt;Probably, it’s because we’re problem solvers at heart, and companies confine us to cubicles and box us in with SCRUM and its ceremonies. And &lt;a href=&quot;/2024/11/18/CodersDontSolveProblems/&quot;&gt;we don’t get to solve problems&lt;/a&gt;. Yes, “ceremony” is the right word.&lt;/p&gt;

&lt;p&gt;When I started coding over 10 years ago, I dreamed of joining a big tech company like Google or Microsoft too. Sliding between offices, having a chef cooking our meals, getting a massage, riding a bike between buildings…&lt;/p&gt;

&lt;p&gt;But, I realized big companies have their own challenges. More middle managers and more office politics. More of being a small cog in a machine. Even smaller cog and larger machine. They’re not the best place for everyone.&lt;/p&gt;

&lt;h2 id=&quot;code-for-money-do-a-goodjob-but-dont-let-your-work-be-your-only-source-of-meaning&quot;&gt;Code for money, do a good job, but don’t let your work be your only source of meaning&lt;/h2&gt;

&lt;p&gt;There’s nothing wrong with working at a coding job just to pay the bills.&lt;/p&gt;

&lt;p&gt;We have to do what we have to do to put a roof over our heads and bread on our tables. If it’s coding, so be it.&lt;/p&gt;

&lt;p&gt;But if we’re coding just for money, we should have hobbies, side projects, and other activities to find a sense of meaning and value outside work. Otherwise, the day job will become a burden. Trust me on this one, I showed up just for money and &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;ended up burned out and sick&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You don’t need “passion” to be a great coder. It’s fine to code just for money and clock out on time. Do a good job, of course. But remember to &lt;a href=&quot;/2025/01/07/DiversifyYourJoy/&quot;&gt;build multiple sources of fulfillment and meaning outside work&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Five Eye-Opening Lessons I Learned from Being Fired from My First Job</title>
   <link href="https://canro91.github.io/2025/01/14/BeingFired/"/>
   <updated>2025-01-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/14/BeingFired</id>
   <content type="html">&lt;p&gt;I was fired from my first job. 10+ years ago.&lt;/p&gt;

&lt;p&gt;My first job taught me A LOT. I had 0 hours of flight time. Everything was new to me. I had to learn about the job and to navigate the corporate world at the same time. But I was fired.&lt;/p&gt;

&lt;p&gt;Yes, fired. Not laid off. Fired. Same result, different cause.&lt;/p&gt;

&lt;p&gt;Did I deserve it? Probably. Did I learn something? Sure.&lt;/p&gt;

&lt;p&gt;Here are five lessons I learned from that:&lt;/p&gt;

&lt;h2 id=&quot;1-you-could-lose-your-job-at-anytime&quot;&gt;1. You could lose your job at anytime&lt;/h2&gt;

&lt;p&gt;It’s obvious now. But it wasn’t 10+ years ago.&lt;/p&gt;

&lt;p&gt;I was fired. But you could also lose your job for reasons you don’t control. A pandemic, a recession, or a layoff. Or your company gets acquired and extinguished. It’s outside of your control.&lt;/p&gt;

&lt;p&gt;When I was in university, I thought being an employee was the safest route. Starting a company was for crazy people. I was sooo wrong. I only needed to be fired once to change my mind.&lt;/p&gt;

&lt;p&gt;What’s truly safe? What you build for yourself: a side business, a rental property, or an investment portfolio.&lt;/p&gt;

&lt;p&gt;Build something you can’t be fired from…Well, Steve Jobs was fired from Apple, so handle with care.&lt;/p&gt;

&lt;h2 id=&quot;2-job-offers-arent-published-anywhere&quot;&gt;2. Job offers aren’t published anywhere&lt;/h2&gt;

&lt;p&gt;A couple of weeks after losing my job, I was interviewing for a small company in my city.&lt;/p&gt;

&lt;p&gt;On the day I left, I had a conversation with my direct boss. Ex-boss, at that point. I don’t remember exactly what we talked about. I don’t know what he saw in me either. Maybe he saw his younger self across the desk that day. But he made a couple of phone calls and arranged an interview for me. It was a lucky day for me after all.&lt;/p&gt;

&lt;p&gt;My first job wasn’t advertised anywhere. I got it because I knew someone who knew someone. And my next job wasn’t advertised either. Again I knew someone who knew someone.&lt;/p&gt;

&lt;p&gt;Our world is moved by connections. By knowing someone who knows someone. Make an extra effort to build your professional network.&lt;/p&gt;

&lt;p&gt;Your network is your most valuable asset.&lt;/p&gt;

&lt;h2 id=&quot;3-accept-the-rules-or-disagree-with-your-feet&quot;&gt;3. Accept the rules or disagree with your feet&lt;/h2&gt;

&lt;p&gt;Talent and hard work aren’t shortcuts to avoid corporate rules.&lt;/p&gt;

&lt;p&gt;There was a new rule at work. I didn’t like it. But I was naive to think anyone would ask me if I liked it and was willing to follow it. HR? My boss? My boss’ boss? Of course, nobody did.&lt;/p&gt;

&lt;p&gt;I was wrong to believe my hard work would exempt me from that rule. Not following that new rule got me fired in the end.&lt;/p&gt;

&lt;p&gt;A job is a game with rules you don’t control. And you have no voice when those rules change. Either you accept those rules or disagree with your feet. Trying to negotiate has no results. And not following them…well, now you know what happened to me.&lt;/p&gt;

&lt;h2 id=&quot;4-listen-to-your-body-it-might-be-telling-you-something&quot;&gt;4. Listen to your body, it might be telling you something&lt;/h2&gt;

&lt;p&gt;It sounds like a cliché. But listen to your body and look for small clues.&lt;/p&gt;

&lt;p&gt;Don’t want to get out of bed to work multiple days in a row? Do Sunday evenings make you anxious anticipating next Monday morning? Your body might be telling you something.&lt;/p&gt;

&lt;p&gt;At some point in my first job, I felt like I was leaving my life behind, sitting at a computer. I was demotivated and disengaged. My morning alarm was torture.&lt;/p&gt;

&lt;p&gt;I didn’t listen to my body. I kept doing the same expecting change without doing anything. The next time I forgot to listen to my body years later, I got burned out and eventually sick.&lt;/p&gt;

&lt;p&gt;Every time I need to make a change in my life, my body tells me when it’s time. “I feel it in my fingers…I feel it in my toes…”&lt;/p&gt;

&lt;h2 id=&quot;5-always-have-an-exit-plan&quot;&gt;5. Always have an exit plan&lt;/h2&gt;

&lt;p&gt;I jumped and left my first job with no plan at all.&lt;/p&gt;

&lt;p&gt;By not making my own plan, I let society choose a plan for me: work hard, get promoted, and get a 3-5% raise every year. Wait to retire. Then, die.&lt;/p&gt;

&lt;p&gt;Take a moment to find out what you want and value the most for your career. Money? Connections? Recognition? Growth? Then, choose the jobs and places that take you closer to that.&lt;/p&gt;

&lt;p&gt;Don’t let others decide by going on auto-pilot. Have your own exit plan. Always.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;After many years, I realized my first job was a launching platform. It put me in the right moment, next to the right people. It started a chain of events that brought me to where I’m now.&lt;/p&gt;

&lt;p&gt;At my first job, I &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;learned some of my most valuable career lessons&lt;/a&gt;. The world isn’t what my teachers told me in university. I had to learn and figure out things on my own. I started my financial journey by making my first deposit into an investment account. I got my first real hours of flight time. I paid for a family dinner with my money for the first time.&lt;/p&gt;

&lt;p&gt;After every ending, there’s a new beginning. Pastures are always greener on the other side.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>I&apos;m Answering the (Bear) Blog Questions Challenge</title>
   <link href="https://canro91.github.io/2025/01/13/BlogQuestionsChallenge/"/>
   <updated>2025-01-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/13/BlogQuestionsChallenge</id>
   <content type="html">&lt;p&gt;I’m not pouring a bucket of cold water over my head, but I’m doing an Internet challenge.&lt;/p&gt;

&lt;p&gt;I found it in &lt;a href=&quot;https://kevquirk.com/blog/blog-questions-challenge&quot;&gt;Kev Quirk’s blog&lt;/a&gt;. And he found it on somebody else’s blog. But the challenge started on Bear Blog. &lt;a href=&quot;https://blog.avas.space/bear-blog-challenge/&quot;&gt;Ava started it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But since I don’t have a blog on that platform, I’m doing this challenge here instead. Just like Kev on his own blog.&lt;/p&gt;

&lt;p&gt;Here I go:&lt;/p&gt;

&lt;h2 id=&quot;why-did-you-start-blogging-in-the-first-place&quot;&gt;Why did you start blogging in the first place?&lt;/h2&gt;

&lt;p&gt;I started blogging as an excuse to become a better coder.&lt;/p&gt;

&lt;p&gt;Probably in the early 2010s, back in &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;my first job&lt;/a&gt;, I googled “how to be a better developer.” And among the many options, I found “start a blog.”&lt;/p&gt;

&lt;p&gt;Then, years later, at my second job, I didn’t want to throw away a couple of hours of Googling while looking for options to finish a task. And that’s how I started writing and blogging.&lt;/p&gt;

&lt;p&gt;I already wrote about &lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;how I started my blog here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;what-platform-are-you-using-to-manage-your-blog-and-why-did-you-choose-it&quot;&gt;What platform are you using to manage your blog and why did you choose it?&lt;/h2&gt;

&lt;p&gt;I use Jekyll on GitHub pages.&lt;/p&gt;

&lt;p&gt;That was the easiest and cheapest alternative I found back in 2018. And, I spent a couple of days, or maybe a week, looking for the right template and theme to start.&lt;/p&gt;

&lt;h2 id=&quot;have-you-blogged-on-other-platforms-before&quot;&gt;Have you blogged on other platforms before?&lt;/h2&gt;

&lt;p&gt;No. I wrote my first online piece ever here.&lt;/p&gt;

&lt;p&gt;But I’ve guest blogged on Exception Not Found and collaborated with two software companies to write on their Medium publications and official sites.&lt;/p&gt;

&lt;p&gt;These days, I cross-post on dev.to and Medium. But my blog is my central hub. All my ideas in some shape or form end up here.&lt;/p&gt;

&lt;h2 id=&quot;how-do-you-write-your-posts-for-example-in-a-local-editing-tool-or-in-a-paneldashboard-thats-part-of-your-blog&quot;&gt;How do you write your posts? For example, in a local editing tool, or in a panel/dashboard that’s part of your blog?&lt;/h2&gt;

&lt;p&gt;I’m a &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;plain text fan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I keep all my drafts, posts, and ideas on Notable. I use a system of tags. I have one tag for post ideas. And once I turn an idea into a post, I use another tag.&lt;/p&gt;

&lt;p&gt;Once a post is ready, I log into GitHub, create a new file, and paste my text there.&lt;/p&gt;

&lt;p&gt;That’s how I blog in a nutshell. But here’s &lt;a href=&quot;/2024/12/15/BloggingWorkflow/&quot;&gt;my blogging workflow&lt;/a&gt; more in-depth.&lt;/p&gt;

&lt;h2 id=&quot;when-do-you-feel-most-inspired-to-write&quot;&gt;When do you feel most inspired to write?&lt;/h2&gt;

&lt;p&gt;Short answer: Anytime. But mostly, in the mornings.&lt;/p&gt;

&lt;p&gt;I tend to write in the mornings. That’s when I feel the most productive and my energy is at its peak. I’m kind of a morning person.&lt;/p&gt;

&lt;p&gt;But, I always have something to write. Inspiration can hit at any time.&lt;/p&gt;

&lt;p&gt;It has happened that right after I put my head on my pillow, an idea comes and I have to rush to write it somewhere. Also, while taking a walk. That’s my &lt;a href=&quot;/2024/12/07/WritersBlock/&quot;&gt;trick to avoid writer’s block&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;do-you-publish-immediately-after-writing-or-do-you-let-it-simmer-a-bit-as-a-draft&quot;&gt;Do you publish immediately after writing, or do you let it simmer a bit as a draft?&lt;/h2&gt;

&lt;p&gt;No. I have a queue of posts. Right now, I have around 20 posts that are ready to publish.&lt;/p&gt;

&lt;p&gt;I start my writing sessions by editing and proofreading my last post. Then I start writing any of my post ideas.&lt;/p&gt;

&lt;h2 id=&quot;whats-your-favorite-post-on-your-blog&quot;&gt;What’s your favorite post on your blog?&lt;/h2&gt;

&lt;p&gt;I’ve written around ~200 posts. I don’t have a favorite. Well, that’s what every mom and dad answer when they’re asked about their favorite child.&lt;/p&gt;

&lt;p&gt;But if I could remove all my posts and keep a handful of them, I’d keep &lt;a href=&quot;/UnitTesting&quot;&gt;my series on Unit Testing&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;any-future-plans-for-your-blog-maybe-a-redesign-a-move-to-another-platform-or-adding-a-new-feature&quot;&gt;Any future plans for your blog? Maybe a redesign, a move to another platform, or adding a new feature?&lt;/h2&gt;

&lt;p&gt;Seth Godin has always inspired me. He’s been writing daily for ~20 years.&lt;/p&gt;

&lt;p&gt;And since &lt;a href=&quot;/2024/11/01/SellingVsHelping/&quot;&gt;November 1st 2024&lt;/a&gt;, I started writing daily about programming and other subjects. In fact, I created a new tag, &lt;a href=&quot;/tags/misc&quot;&gt;/misc&lt;/a&gt;, to dump posts about all other subjects. My challenge is to write 100 daily posts. If I stick to my rhythm, &lt;a href=&quot;/2025/02/09/100DailyPosts/&quot;&gt;I’m done next February 9th&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Chances are I’m keeping the daily rhythm after those 100 posts.&lt;/p&gt;

&lt;p&gt;I’m too lazy to redesign my blog. I’m a black-and-white fan, so I’m sticking to the left pane. I’d like to add a scrolling bar and include more menu entries.&lt;/p&gt;

&lt;p&gt;Oh! My own domain. I tried to buy one in the past. And I found out there’s a soccer player, a singer, and a director with my name.&lt;/p&gt;

&lt;p&gt;I’d like to give Substack a try, but it would be for anything else apart from coding.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;I owe my career growth to &lt;a href=&quot;/2024/10/14/LearningLanguages/&quot;&gt;learning foreign languages&lt;/a&gt; and writing.&lt;/p&gt;

&lt;p&gt;Writing has opened doors for me. I made my first side income thanks to my blog. I skipped the hiring line when I applied to my last job.&lt;/p&gt;

&lt;p&gt;In fact, I don’t have a coding portfolio. &lt;a href=&quot;/2025/01/12/BlogVsPortfolio/&quot;&gt;My blog has done more for me&lt;/a&gt;. Always &lt;a href=&quot;/2024/12/06/AlwaysWriting/&quot;&gt;write about what you do at your work&lt;/a&gt;. That’s better than an old-fashioned CV.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Blog Has Been Better for My Career Than a Portfolio</title>
   <link href="https://canro91.github.io/2025/01/12/BlogVsPortfolio/"/>
   <updated>2025-01-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/12/BlogVsPortfolio</id>
   <content type="html">&lt;p&gt;I don’t have a coding portfolio.&lt;/p&gt;

&lt;p&gt;By portfolio, I mean a webpage showcasing my best projects. My GitHub account is the closest thing to a coding portfolio. But it hasn’t helped me land jobs.&lt;/p&gt;

&lt;p&gt;My blog has helped me more. Here’s how:&lt;/p&gt;

&lt;h2 id=&quot;1-a-content-collaboration&quot;&gt;1. A content collaboration&lt;/h2&gt;

&lt;p&gt;Some time ago, the next day after interviewing for a small local company, I got a phone call.&lt;/p&gt;

&lt;p&gt;They wanted me to start a company blog for them. The interviewer read some of my blog posts. I had a link to it on my CV. He wanted me to write something similar for them.&lt;/p&gt;

&lt;p&gt;I wrote five blog posts for them with interview preparation material. Even though I decided not to continue the interview process, I declared it a win. Also, I made some lunch money with them.&lt;/p&gt;

&lt;h2 id=&quot;2-a-smoother-hiring-process&quot;&gt;2. A smoother hiring process&lt;/h2&gt;

&lt;p&gt;Years later, the last time I applied for a job, in the first interview, I shared my screen and walked the interviewer through my blog.&lt;/p&gt;

&lt;p&gt;The interviewer asked if I contributed somehow to the coding community. The process went smoother from there. I didn’t have any coding interview after that, except for opening a PR on an open source project. That was it.&lt;/p&gt;

&lt;p&gt;Sharing your thoughts and learnings can open unexpected doors. Even if you don’t land new jobs, you’ll learn new skills. And more importantly, you’ll learn to &lt;a href=&quot;/2024/11/08/WhyWriting/&quot;&gt;think better&lt;/a&gt;. So write your first online piece and see what opportunities it brings. But &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;don’t start a blog&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Ditching My To-Do List: Here&apos;s What I&apos;m Doing Instead</title>
   <link href="https://canro91.github.io/2025/01/11/DitchingTodos/"/>
   <updated>2025-01-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/11/DitchingTodos</id>
   <content type="html">&lt;p&gt;I used to be a productivity freak with to-do lists.&lt;/p&gt;

&lt;p&gt;At a past job, I had a notebook where I wrote down every single action I needed to finish. Small tasks, large tasks, meeting notes… Everything.&lt;/p&gt;

&lt;p&gt;Later on, I became a plain text lover and kept my to-do list in a .txt file I edited with Notepad: a “todo.txt” file.&lt;/p&gt;

&lt;p&gt;Same story as my notebook at my past job. Even worse. I had “todo.txt” files for work and non-work. One in my personal computer and another in my work computer. My to-do lists grew without control. I still have items in my “todo.txt” file from years ago. Oops!&lt;/p&gt;

&lt;h2 id=&quot;but-theres-something-with-to-do-lists&quot;&gt;But there’s something with to-do lists.&lt;/h2&gt;

&lt;p&gt;To-do lists are stressful.&lt;/p&gt;

&lt;p&gt;Every item on our to-do lists is an item we haven’t finished and probably won’t finish either. In the meantime, they’re another source of stress.&lt;/p&gt;

&lt;p&gt;I learned from &lt;a href=&quot;/2025/01/19/ChooseYourselfGuideToWealth/&quot;&gt;Choose Yourself Guide to Wealth&lt;/a&gt; by James Altucher to ditch to-do lists and goals, and live by themes instead.&lt;/p&gt;

&lt;p&gt;The theme I want to live by in 2025 is the Daily Practice: doing something for my mind, body, and spirit every day.&lt;/p&gt;

&lt;h2 id=&quot;as-part-of-my-daily-practice-im-sticking-to&quot;&gt;As part of my Daily Practice, I’m sticking to:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;A glass of water after waking up&lt;/li&gt;
  &lt;li&gt;A workout session&lt;/li&gt;
  &lt;li&gt;A moment of silence&lt;/li&gt;
  &lt;li&gt;Writing 200-250 words&lt;/li&gt;
  &lt;li&gt;Coming up with 10 ideas&lt;/li&gt;
  &lt;li&gt;Getting rid of negativity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Few things have helped me change my life like the Daily Practice.&lt;/p&gt;

&lt;p&gt;That Daily Practice helped me get my health and life on track after burning out. Focusing on my health brought clarity to my life last year.&lt;/p&gt;

&lt;p&gt;Goodbye “todo.txt” file and hello Daily Practice. That’s my only rule for 2025. You should give it a try too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>If You Enjoy Coding, Think Twice About Joining the Management Track</title>
   <link href="https://canro91.github.io/2025/01/10/JoiningManagementTrack/"/>
   <updated>2025-01-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/10/JoiningManagementTrack</id>
   <content type="html">&lt;p&gt;It took me 10 years to learn this lesson:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The higher up you go, the less it’s about coding and more about all other skills.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Being the best at coding won’t get you higher on the corporate ladder. Well, the corporate ladder is a trap.&lt;/p&gt;

&lt;p&gt;Unfortunately, few places offer growth opportunities for coders, and even fewer for those who don’t want the management track.&lt;/p&gt;

&lt;p&gt;Every place has its own expectations for team leaders or managers.&lt;/p&gt;

&lt;p&gt;In some places, the team leader role is divided between:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;someone technical in charge of coding and architecture decisions, and&lt;/li&gt;
  &lt;li&gt;someone non-technical in charge of project management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other places, a team leader wears all hats, often for the same pay.&lt;/p&gt;

&lt;h2 id=&quot;making-the-jump-into-a-leadership-role&quot;&gt;Making the jump into a leadership role&lt;/h2&gt;

&lt;p&gt;If you enjoy coding and are thinking about joining the management track, start by understanding that your role as a team leader is more like a movie director than an actor.&lt;/p&gt;

&lt;p&gt;Your job is not to appear on screen, except for some cameos. Your job is to make sure your movie gets done as expected and on time.&lt;/p&gt;

&lt;p&gt;For that, you’ll need coding skills. Sure. But much stronger soft skills.&lt;/p&gt;

&lt;p&gt;You’ll spend most of your time in meetings, not coding:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Daily meetings with your team,&lt;/li&gt;
  &lt;li&gt;Daily meetings with all other leaders,&lt;/li&gt;
  &lt;li&gt;1-on-1s with every team member,&lt;/li&gt;
  &lt;li&gt;Sync ups with project managers and product people&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you understand you’re the director, not the best actor, show your interest in exploring the role with your team leader during your 1-on-1s or performance reviews.&lt;/p&gt;

&lt;p&gt;Then, find easy and cheap ways to validate if being a team leader is a role you’d enjoy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Organize and tidy up your project board&lt;/li&gt;
  &lt;li&gt;Cover your team leader during their vacation&lt;/li&gt;
  &lt;li&gt;Be the onboarding buddy for new team members&lt;/li&gt;
  &lt;li&gt;Coordinate efforts to complete a feature from requirements to deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That will force you out of your “coding” comfort zone into the soft skill-heavy zone.&lt;/p&gt;

&lt;p&gt;As a leader, you’re not responsible for your own code anymore. You’re responsible for all other coders and the code they write.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Stalking a Writer Online Made Me Change My Reading Strategy</title>
   <link href="https://canro91.github.io/2025/01/09/ReadingStrategy/"/>
   <updated>2025-01-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/09/ReadingStrategy</id>
   <content type="html">&lt;p&gt;I’m a recovered book addict.&lt;/p&gt;

&lt;p&gt;Two years ago, I tried to read as many books as possible to show off my huge book count. But I didn’t remember much about those books, even when I took notes.&lt;/p&gt;

&lt;p&gt;Ironically, it was another book, &lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;The Almanack of Naval Ravikant&lt;/a&gt;, that made me change my mind.&lt;/p&gt;

&lt;p&gt;By pure accident, I discovered my new reading strategy.&lt;/p&gt;

&lt;p&gt;Last year, for the first time, I discovered &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I found his bankruptcy stories. I followed his idea of &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;becoming an Idea Machine&lt;/a&gt;. I read some of his books. I loved his idea of the Daily Practice. And without even realizing it, I was stalking James Altucher online. Quora, Medium, his own website, YouTube… Everywhere.&lt;/p&gt;

&lt;p&gt;By immersing myself in his work, I heard extra details about the stories in his books. I learned his book publishing strategy. I learned about the motivation behind some of those books. And I listened to way more anecdotes.&lt;/p&gt;

&lt;p&gt;So, from now on, I’m immersing myself in a writer’s world by reading their books and listening to their interviews. It adds more context and depth to my reading experience. Yes, often listening to an interview about a book counts as reading the book itself.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>As a Team Leader, You&apos;re Not the Best Coder Anymore</title>
   <link href="https://canro91.github.io/2025/01/08/BeingATeamLeader/"/>
   <updated>2025-01-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/08/BeingATeamLeader</id>
   <content type="html">&lt;p&gt;Promoting the best coder to team leader is how projects go sideways.&lt;/p&gt;

&lt;p&gt;I’ve seen it happen. One day, an executive pats the best coder’s shoulder three times. And the next day, he’s a team leader. No training. No expectations shared. Just a new title, a team, and lots of meetings.&lt;/p&gt;

&lt;p&gt;That new leader continues thinking in terms of lines of code and pull requests, failing to delegate, pass context, and coordinate a team.&lt;/p&gt;

&lt;p&gt;The next thing you know, the new team leader burns out and leaves the company. The project? Behind schedule and without a leader.&lt;/p&gt;

&lt;p&gt;Being good at coding opens doors for leadership roles. But to shine as a leader, you need stronger soft skills.&lt;/p&gt;

&lt;p&gt;You’re not the best player on the team anymore. Now you’re the team coach. You’re not supposed to be the fastest runner or the best at kicking. You’re supposed to manage a team.&lt;/p&gt;

&lt;p&gt;Soft skills are way more important than coding because the higher up you climb the ladder, the less it’s about coding and the more about communication, project management, and team dynamics.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>You Need Multiple Sources of Joy—A Job Alone Isn&apos;t an Option</title>
   <link href="https://canro91.github.io/2025/01/07/DiversifyYourJoy/"/>
   <updated>2025-01-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/07/DiversifyYourJoy</id>
   <content type="html">&lt;p&gt;Diversify your sources of purpose and joy, the same way you should diversify your income.&lt;/p&gt;

&lt;p&gt;Otherwise, if you lose your only source of purpose and joy, you’ll feel lost.&lt;/p&gt;

&lt;h2 id=&quot;this-happened-to-vinay-hiremath-co-founder-of-loom--the-online-screen-recording-tool&quot;&gt;This happened to Vinay Hiremath, co-founder of Loom- the online screen recording tool.&lt;/h2&gt;

&lt;p&gt;After selling his company, &lt;a href=&quot;https://vinay.sh/i-am-rich-and-have-no-idea-what-to-do-with-my-life/&quot;&gt;he’s rich and has no idea what to do with his life&lt;/a&gt;. He wrote about it on his blog. All of a sudden the “co-founder of Loom” was gone.&lt;/p&gt;

&lt;p&gt;With lots of cash but no sense of purpose, he went hiking, broke up with his girlfriend, went to the Himalayas, tried getting to Washington, and on and on. All in an effort to find a sense of meaning.&lt;/p&gt;

&lt;h2 id=&quot;while-i-havent-had-an-exit-yet-i-found-myself-going-through-something-a-similar-situation&quot;&gt;While I haven’t had an exit yet, I found myself going through something a similar situation.&lt;/h2&gt;

&lt;p&gt;I had my sense of value attached to my job. “Software Engineer.” For some time, that was everything I was and did… until &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;I lost my job after a layoff&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All of a sudden I didn’t have the title. I wasn’t doing any coding. And I didn’t want to do any more of it. I felt trapped in a label I decided to accept on my own. “Coder” and “Software Engineer.”&lt;/p&gt;

&lt;p&gt;During that forced time off, I took the opportunity to take care of my health, get back to my hobbies, and &lt;a href=&quot;/2025/01/04/RealizationsFrom2024/&quot;&gt;reflect on what I truly wanted in life&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I realized that, above all job titles, I’m a learner. Software engineering? It was just a stint of that.&lt;/p&gt;

&lt;h2 id=&quot;if-youre-in-a-similar-situation&quot;&gt;If you’re in a similar situation,&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Understand you’re not your job. You’re not a title. You are way more than that.&lt;/li&gt;
  &lt;li&gt;Find new hobbies outside work. Learn new skills just for fun. Read books from different fields. Practice a physical activity. Do something with your hands, away from screens.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remember, your purpose and joy shouldn’t come from just one place. Don’t wait until you feel lost. Start today. Diversify your sources of purpose and joy.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>10 Inspiring and Thought-Provoking Quotes About Life and Money I Collected in 2024</title>
   <link href="https://canro91.github.io/2025/01/06/BestQuote2024/"/>
   <updated>2025-01-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/06/BestQuote2024</id>
   <content type="html">&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1504314452987-63eb3a6927b1?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxNTg4MTM4Nw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;girl wearing a t-shirt with a quote in it&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@lishakov?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Andrej Lišakov&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/woman-holding-denim-jacket--r4n8oBR_-Y?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I’m not a quote addict. And I don’t collect quotes for a living either.&lt;/p&gt;

&lt;p&gt;These weren’t intended to be quotes. But they’re pieces of wisdom I noticed and wrote down when consuming content as part of my daily procrastination. Sorry, I meant catching up.&lt;/p&gt;

&lt;h2 id=&quot;from-james-altucherwriter-entrepreneur-and-investor&quot;&gt;From James Altucher—Writer, entrepreneur, and investor&lt;/h2&gt;

&lt;p&gt;Last year, &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;I stalked James Altucher&lt;/a&gt;. I read his books and watched his interviews. I found his story and writings raw and inspiring.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Money is a by-product of personal development&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Money is only one part of an abundant life.&lt;/p&gt;

&lt;p&gt;And your life is abundant when your physical, mental, spiritual, and emotional bodies are healthy. You’re abundant when you move your body, do something creative, share ideas, and live free of negativity every single day.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Little things show how people really are&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s not the speeches, interviews, and business presentations. It’s how you treat a waitress, your coworkers, or the janitor.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Don’t do things you hate at the expense of things you love&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was when I tried to convince myself of showing up to work just for money that I got burned out. I tried to convince myself to take that money to build a runway for my side hustle. I was so wrong!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Have multiple sources of joy&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When things go sideways in one area, you can jump to another and still find joy and meaning. Diversify your joy the same way you should diversify your income sources.&lt;/p&gt;

&lt;h2 id=&quot;taylor-cavanaughex-seal-and-life-coach&quot;&gt;Taylor Cavanaugh—ex-SEAL and life coach&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Anything you do starts with self-development&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one comes from a Navy SEAL turned into a French Legionnaire. The media has created this glorified image of SEALs like indestructible and focused men.&lt;/p&gt;

&lt;p&gt;But this isn’t the case. Taylor’s life was a disaster. All his bad decisions led him to escape to another country.&lt;/p&gt;

&lt;p&gt;After overcoming his messy life, now he’s a life coach. And that quote summarizes his journey through recovery. It all starts in your mind with your beliefs.&lt;/p&gt;

&lt;h2 id=&quot;seth-godinentrepreneur-and-writer&quot;&gt;Seth Godin—Entrepreneur and writer&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Big problems demand small solutions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You don’t go to bed and wake up with a dream life. It requires effort and consistency. And it starts with the desire to change and small simple steps in the long run.&lt;/p&gt;

&lt;h2 id=&quot;dan-koewriter&quot;&gt;Dan Koe—Writer&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;The goal is getting paid by being you&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You don’t need to close your eyes and try hard to come up with a niche. You’re the niche. Your interests, vision, and goals make you the niche.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Make the content you want to see in the world&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The internet isn’t saturated. Sure, it’s getting flooded with more AI-generated content every day. But we’re craving more genuine and human content. Create that.&lt;/p&gt;

&lt;h2 id=&quot;jim-kwikbrain-coach&quot;&gt;Jim Kwik—Brain coach&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;A confused mind accomplishes nothing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In 2023, I burned out. It took me months to get back on track. I was so deeply rooted in my frustration and resentment that I couldn’t see a way out. It was only when I started to work on my health and change my internal voice that I found a way out.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Reading is the best exercise for your brain&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reading is the closest we are to plugging ourselves into a computer and downloading decades of information to our brains in seconds. Reading is the Neo’s “I know Kung fu” from The Matrix.&lt;/p&gt;

&lt;p&gt;Ok, I said I’m not a quote collector. But I have to confess I have one or two quotes beautifully framed around my desk. Whether you hang quotes on your wall or not, what truly matters is turning that inspiration into beliefs, actions, and habits.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>You Only Need These Four Books To Change Your Relationship With Money</title>
   <link href="https://canro91.github.io/2025/01/05/MoneyBooks/"/>
   <updated>2025-01-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/05/MoneyBooks</id>
   <content type="html">&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1574607383172-1421479aec9d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxNTg4MTM4Nw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;girl holding 1 U.S. dollar banknote&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@anniespratt?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Annie Spratt&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/girl-holding-1-us-dollar-banknote-BJ3g2Ck2WfE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Money is one of the skills we have to learn by ourselves.&lt;/p&gt;

&lt;p&gt;I didn’t have a class called “Money” in high school. And if you went to a regular high school like me, you didn’t either.&lt;/p&gt;

&lt;p&gt;Like it or not, we live in a world run by money. And still, we don’t learn a thing about money.&lt;/p&gt;

&lt;p&gt;What we know about money is what we absorb while watching our parents sitting at the dinner table discussing or arguing about money. And they learned about money by watching their parents too.&lt;/p&gt;

&lt;p&gt;I felt the urgency to learn about money when I started to earn a high salary as a software engineer, higher than all my previous salaries.&lt;/p&gt;

&lt;p&gt;The fear of letting money slip out of my hands led me to watch finance videos on YouTube. One video led to another one, and videos led to books.&lt;/p&gt;

&lt;p&gt;If you want to start your money journey, start with these four books:&lt;/p&gt;

&lt;h2 id=&quot;1-the-richest-man-in-babylon-by-george-clason&quot;&gt;1. The Richest Man in Babylon by George Clason&lt;/h2&gt;

&lt;p&gt;If you want to only read one book from this list, read this one.&lt;/p&gt;

&lt;p&gt;This book takes you to ancient Babylon and uses fables to teach you money lessons. It shows the power of stories. You will picture yourself in the middle of the desert, sitting in a caravan, listening to money lessons.&lt;/p&gt;

&lt;p&gt;We don’t trade gold coins for food and live in cities enclosed by walls anymore. But you can easily translate the lessons in this book to our day and age.&lt;/p&gt;

&lt;p&gt;If I could summarize this book in one sentence, it would be:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Ask the jeweler for jewelry advice.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Often we go to our friends and relatives with poor financial education to ask about money. That’s an expensive mistake!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Lesson&lt;/strong&gt;: Pay yourself first and save 10% of your income.&lt;/p&gt;

&lt;h2 id=&quot;2-rich-dad-poor-dad-by-robert-kiyosaki&quot;&gt;2. Rich Dad Poor Dad by Robert Kiyosaki&lt;/h2&gt;

&lt;p&gt;This won’t be a complete list of money books if I don’t include this one.&lt;/p&gt;

&lt;p&gt;It doesn’t matter if Robert Kiyosaki’s rich dad is real or not.&lt;/p&gt;

&lt;p&gt;Again, the power of stories to share lessons. It tells what his best friend’s dad, the rich dad, taught the author and his best friend about money.&lt;/p&gt;

&lt;p&gt;The rich dad taught them not to work for money but for experience and lessons. And the poor dad taught them to go to school, get good grades, and get a job.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“JOB is an acronym for ‘Just Over Broke’”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The lesson I remember the most from this book is the employee/employer dilemma. Employees do the minimum not to get fired and employers pay their employees the minimum so they don’t leave. They’re always in a conflict of interest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Lesson&lt;/strong&gt;: An asset puts money into your pockets and a liability takes money out of your pockets. To be truly rich, buy assets, not liabilities that look like assets.&lt;/p&gt;

&lt;p&gt;Repeat out loud: if it doesn’t put money into my pockets, my car is not an asset.&lt;/p&gt;

&lt;h2 id=&quot;3-simple-path-to-wealth-by-jl-collins&quot;&gt;3. Simple Path to Wealth by JL Collins&lt;/h2&gt;

&lt;p&gt;This book is a collection of letters and posts the author wrote to his daughter to teach her about money and the Stock Market.&lt;/p&gt;

&lt;p&gt;The premise of this book is that some young people don’t care much about money, so they need a simple plan to build and preserve wealth.&lt;/p&gt;

&lt;p&gt;This is the book that taught me about emergency funds. Well, the book called it “F*ck-you money.” Some savings to keep the boat floating when you don’t tolerate your job anymore and just want to tell your boss the big F-word or when the decision to leave is not yours.&lt;/p&gt;

&lt;p&gt;Thanks to an emergency fund, I kept my family’s boat floating for months after an unexpected layoff until I found something else.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“[Before picking stocks], ask yourself this simple question: ‘Am I Warren Buffet?’ If the answer is ‘no,’ keep your feet firmly on the ground with indexing.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The author is a fan of low-cost index funds and that’s the approach he teaches in the book. Stick with a total market index fund and invest regularly the same amount no matter what disaster or recession “experts” are forecasting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Lesson&lt;/strong&gt;: Stay away from debt, keep your expenses in check, and invest in broad index funds like the Vanguard Total Stock Market Index Fund ETF (VTI) and 
Vanguard S&amp;amp;P 500 ETF (VOO).&lt;/p&gt;

&lt;p&gt;Against the author’s teachings, I have to confess I added a second fund on dividends to my portfolio. Sorry!&lt;/p&gt;

&lt;h2 id=&quot;4-psychology-of-money-by-morgan-housel&quot;&gt;4. Psychology of Money by Morgan Housel&lt;/h2&gt;

&lt;p&gt;This one is less about the mechanics of saving and investing. It covers 20 lessons about money, all of them around the premise that money is a soft skill.&lt;/p&gt;

&lt;p&gt;Money isn’t about rational decisions we make in front of an Excel file. It’s more about the emotional decisions we make at the dinner table.&lt;/p&gt;

&lt;p&gt;The story I remember the most from this book is the one about the janitor who went unnoticed until he died, and his net worth was revealed. He was a multi-millionaire who donated part of his fortune to the local school and library. In his lifetime, he kept saving and investing his modest salary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Lesson&lt;/strong&gt;: Money is not what you can see: fancy watches and expensive cars. Money is the assets that haven’t been made into fancy watches and expensive cars yet.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;You could read more books and watch more YouTube videos. Some are more practical, others more philosophical.&lt;/p&gt;

&lt;p&gt;But all of them have in common that money is a mindset and a skill we can master like any other skill.&lt;/p&gt;

&lt;p&gt;Money isn’t evil. Money is what we decide to be. It’s a multiplier of our values. Remember: “If it’s in our heads first, it won’t be in our hands.”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Got Laid Off and Recovered From Burnout in 2024. Here are 13 Lessons and Realizations</title>
   <link href="https://canro91.github.io/2025/01/04/RealizationsFrom2024/"/>
   <updated>2025-01-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/04/RealizationsFrom2024</id>
   <content type="html">&lt;ol&gt;
  &lt;li&gt;Writing is free therapy.&lt;/li&gt;
  &lt;li&gt;Detach your sense of meaning from your work.&lt;/li&gt;
  &lt;li&gt;You don’t need to find a “passion” to succeed.&lt;/li&gt;
  &lt;li&gt;Working on your health brings clarity to your life.&lt;/li&gt;
  &lt;li&gt;You are not your job title. You’re way more than that.&lt;/li&gt;
  &lt;li&gt;Do what you can control and let go of everything else.&lt;/li&gt;
  &lt;li&gt;Stop chasing a fancy title. Optimize for a lifestyle instead.&lt;/li&gt;
  &lt;li&gt;Any change in your life starts in your mind with your beliefs.&lt;/li&gt;
  &lt;li&gt;Build multiple income sources right now. Your life depends on it.&lt;/li&gt;
  &lt;li&gt;Start and double down on what works. There won’t be a perfect time to start.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/12/30/BestProductivityHack/&quot;&gt;Eat to have more energy and think better&lt;/a&gt;. You are what you put in your body.&lt;/li&gt;
  &lt;li&gt;The right daily routine and habits can change your life. The wrong ones can destroy it.&lt;/li&gt;
  &lt;li&gt;Your health and well-being are more important than a job. You can always find a new job. But a new body? Not that easy!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If there’s one habit I’m sticking to in 2025, it is taking care of my body, mind, and spirit every single day.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I Correct Mistakes When Practicing Foreign Languages</title>
   <link href="https://canro91.github.io/2025/01/03/SpeakingMistakes/"/>
   <updated>2025-01-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/03/SpeakingMistakes</id>
   <content type="html">&lt;p&gt;It’s discouraging when a teacher corrects your mistakes in every phrase. It makes you want to stop speaking.&lt;/p&gt;

&lt;p&gt;I’ve had all types of teachers:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The ones who cut you off in the middle of every sentence to correct you&lt;/li&gt;
  &lt;li&gt;The ones who write corrections on a whiteboard&lt;/li&gt;
  &lt;li&gt;The ones who don’t correct at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need the right balance when learning a new language.&lt;/p&gt;

&lt;p&gt;If we’re not corrected, we keep making the same mistakes. But if we get corrected all the time, we lose confidence.&lt;/p&gt;

&lt;p&gt;After taking many language courses and doing plenty of language exchanges, here’s my strategy for correcting mistakes:&lt;/p&gt;

&lt;p&gt;Instead of cutting off your language partner (or student) in the middle of a sentence to point out a mistake, restate the sentence or ask a follow-up question without the mistake.&lt;/p&gt;

&lt;p&gt;For example, if someone says “my brother don’t like apples,” you could say:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You mean your brother doesn’t like apples?&lt;/li&gt;
  &lt;li&gt;Why is that? Why doesn’t your brother like apples?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re correcting the mistake while keeping the conversation going and without discouraging the other person with too many corrections.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>If You&apos;re Starting a Creative Project, Remember This Law</title>
   <link href="https://canro91.github.io/2025/01/02/SturgeonLaw/"/>
   <updated>2025-01-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/02/SturgeonLaw</id>
   <content type="html">&lt;p&gt;90% of everything is crap.&lt;/p&gt;

&lt;p&gt;And I’m not making that up. It’s Sturgeon’s Law.&lt;/p&gt;

&lt;p&gt;That’s not discouraging. It’s relieving, at least for me.&lt;/p&gt;

&lt;p&gt;It lowers our expectations when starting any creative project, like writing or painting, for the first time.&lt;/p&gt;

&lt;h2 id=&quot;our-first-posts-paintings-or-pictures-will-be-in-that-90&quot;&gt;Our first posts, paintings, or pictures will be in that 90%&lt;/h2&gt;

&lt;p&gt;My first post was in the 90% of crap.&lt;/p&gt;

&lt;p&gt;I wrote my first blog post back in 2018. And “post” is a strong word. It was a word vomit. I dumped a bunch of words into a document and published it online. I still keep that post unedited to remind me how I started.&lt;/p&gt;

&lt;p&gt;My first LinkedIn post was in the 90% of crap too.&lt;/p&gt;

&lt;p&gt;I made all LinkedIn sins possible in a single post:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Only an external link.&lt;/li&gt;
  &lt;li&gt;Hashtags and emojis.&lt;/li&gt;
  &lt;li&gt;No whitespace for readability.&lt;/li&gt;
  &lt;li&gt;Zero formatting for mobile devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-way-out-of-that-90&quot;&gt;The way out of that 90%?&lt;/h2&gt;

&lt;p&gt;Keep showing up and keep improving.&lt;/p&gt;

&lt;p&gt;Think of every repetition, post or painting, like a small experiment. Small experiments give you room to explore, to break the rules, to see what happens. If an experiment failed, you’ll always have a new experiment the next day.&lt;/p&gt;

&lt;h2 id=&quot;how-do-i-know-im-improving&quot;&gt;How do I know I’m improving?&lt;/h2&gt;

&lt;p&gt;If you find your first repetitions cringe-worthy, you’re improving.&lt;/p&gt;

&lt;p&gt;If, after months of repetitions, you don’t cringe when going back to your first posts or paintings, you haven’t improved much. You need to do more experiments.&lt;/p&gt;

&lt;p&gt;90% of everything is crap. That’s encouraging. We only need a bit of effort to get ahead of that 90%.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Best of 2024: My Year in Review</title>
   <link href="https://canro91.github.io/2025/01/01/BestOf2024/"/>
   <updated>2025-01-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2025/01/01/BestOf2024</id>
   <content type="html">&lt;p&gt;If I had to define my 2024 in one word, it would be: Change.&lt;/p&gt;

&lt;p&gt;In 2023, I got burned out and sick. And when I was laid off in early 2024, I felt both worried and relieved. I needed some time off.&lt;/p&gt;

&lt;p&gt;That layoff taught me valuable lessons about life, career, and money.&lt;/p&gt;

&lt;h2 id=&quot;my-coding-side&quot;&gt;My coding side&lt;/h2&gt;

&lt;p&gt;2024 felt like a rollercoaster.&lt;/p&gt;

&lt;p&gt;After getting laid off and sending dozens of applications, I decided to take some time off to take care of my health. A mini-retirement.&lt;/p&gt;

&lt;p&gt;After the radio silence and a few “thanks, maybe later” replies, I started freelancing with a small software agency. That helped me stay afloat for a month or two without running out of savings.&lt;/p&gt;

&lt;p&gt;In 2024, I doubled down on my online presence.&lt;/p&gt;

&lt;p&gt;I created two video courses on Udemy: &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot;&gt;here&lt;/a&gt;. I hired Microsoft’s &lt;a href=&quot;/2024/03/18/AIToLaunchMyCourses/&quot;&gt;Copilot as my assistant&lt;/a&gt; to create the promotional materials. All the content and recordings were made by me. A human.&lt;/p&gt;

&lt;h2 id=&quot;my-career-reflections&quot;&gt;My career reflections&lt;/h2&gt;

&lt;p&gt;Being laid off gave me time to reflect on my career after over 10 years of non-stop work.&lt;/p&gt;

&lt;p&gt;I wrote about the &lt;a href=&quot;/2024/09/02/LessonsFromMyFirstCodingJob/&quot;&gt;lessons my first job taught me about coding and life&lt;/a&gt; and shared &lt;a href=&quot;/2024/06/24/LessonsForAMentee/&quot;&gt;8 lessons for new developers, like in a free mentoring session&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last year the coding world went nuts when Devin, “the first AI software engineer,” was released and I gave my predictions for &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;coding in 2034&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re reading this from the future, let me know if I nailed it with my predictions. By the way, do you have flying cars or are you still dreaming about them?&lt;/p&gt;

&lt;p&gt;I created &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot;&gt;a 7-day email course&lt;/a&gt; to share the lessons I wish I had known to survive a career in software engineering. I share what I’d tell my younger self starting his first professional coding job.&lt;/p&gt;

&lt;p&gt;And I &lt;a href=&quot;/2024/10/10/EmailList/&quot;&gt;moved my Monday Links series to an email list&lt;/a&gt;. Every other week, you won’t get Monday Links, but Friday Links in your inbox. For free.&lt;/p&gt;

&lt;h2 id=&quot;my-most-read-posts&quot;&gt;My most read posts&lt;/h2&gt;

&lt;p&gt;If you missed any of them, here are my five most read posts from 2024:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/04/01/NET8FakeLogger/&quot;&gt;How to Test Logging Messages with FakeLogger&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;It Seems the C# Team Is Finally Considering Supporting Discriminated Unions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/06/10/TestingTimeWithTimeProvider/&quot;&gt;Testing DateTime.Now Revisited: .NET 8.0 TimeProvider&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/04/15/NET9LinqMethods/&quot;&gt;Two new LINQ methods in .NET 9: CountBy and Index&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;I applied at a FAANG and failed: Three interviewing lessons&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;my-writing-side&quot;&gt;My writing side&lt;/h2&gt;

&lt;p&gt;I will remember 2024 as the year I went all in on my writing.&lt;/p&gt;

&lt;p&gt;After writing for more than 5 years, I took my first writing class. That gave me momentum to keep the writing ball rolling. I even created a &lt;a href=&quot;/tags/misc/&quot;&gt;tag /misc&lt;/a&gt; to write about everything outside programming and software engineering.&lt;/p&gt;

&lt;p&gt;Starting on November 1st, I began writing daily on my blog. Those daily posts fueled my writing everywhere else.&lt;/p&gt;

&lt;h3 id=&quot;linkedin&quot;&gt;LinkedIn&lt;/h3&gt;

&lt;p&gt;After months of inactivity in 2023, I revived my LinkedIn account from Zombieland.&lt;/p&gt;

&lt;p&gt;I challenged myself to write 100 short-form native posts. I started with 1 post a week, then 2, then 3… I doubled my follower count and had two or three “viral” posts. Those are vanity metrics. But the main benefit? &lt;a href=&quot;/2024/12/02/FearOfPublishing/&quot;&gt;The fear of writing in social media is gone!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It turns out LinkedIn is not that cringy when we follow the right strategy.&lt;/p&gt;

&lt;h3 id=&quot;devto&quot;&gt;dev.to&lt;/h3&gt;

&lt;p&gt;In 2024, I kept reposting some of my posts on dev.to.&lt;/p&gt;

&lt;p&gt;The dev.to team featured three of my posts in the Top7 posts of the week. These ones:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.to/canro91/four-lessons-my-first-job-as-a-software-engineer-taught-me-about-coding-and-life-2em5&quot;&gt;Four Lessons My First Job as a Software Engineer Taught Me About Coding and Life &lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.to/canro91/i-applied-at-a-faang-and-failed-three-interviewing-lessons-1a4k&quot;&gt;I Applied at a FAANG and Failed — Three Interviewing Lessons&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.to/canro91/i-dont-use-pushy-questions-in-code-reviews-anymore-this-is-what-i-do-instead-58j3&quot;&gt;I Don’t Use “Pushy” Questions in Code Reviews Anymore. This Is What I Do Instead &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;Taking some time off to take care of my health was the best decision I made in 2024. Being physically healthy spreads to all other areas of our life.&lt;/p&gt;

&lt;p&gt;And definitely, Monday mornings don’t feel the same when you wake up to do something you love.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding in 2025!&lt;/p&gt;

&lt;p&gt;Don’t miss my &lt;a href=&quot;/2024/01/08/BestOf2023/&quot;&gt;best of 2023&lt;/a&gt;, &lt;a href=&quot;/2023/01/09/BestOf2022/&quot;&gt;2022&lt;/a&gt;, and &lt;a href=&quot;/2022/01/10/BestOf2021/&quot;&gt;2021&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Best Way to Get Better at Writing Code Isn&apos;t Just Writing More Code</title>
   <link href="https://canro91.github.io/2024/12/31/GetBetterAtCoding/"/>
   <updated>2024-12-31T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/31/GetBetterAtCoding</id>
   <content type="html">&lt;p&gt;More than 10 years ago, I started my real coding journey with a Google search: “How to be a better developer.”&lt;/p&gt;

&lt;p&gt;University had taught me a lot of things I didn’t need. I had to teach myself the ones I needed.&lt;/p&gt;

&lt;p&gt;I went down the rabbit hole. That search gave me lots of ideas and inspiration:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Write specs&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Write unit tests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Document your code&lt;/li&gt;
  &lt;li&gt;Learn Functional Programming&lt;/li&gt;
  &lt;li&gt;Write self-documenting code&lt;/li&gt;
  &lt;li&gt;Read &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Start a blog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah! Starting a blog. That inspired me to start this very blog. I didn’t know I was about to enjoy writing so much.&lt;/p&gt;

&lt;h2 id=&quot;those-are-good-ideas-but-what-has-worked-best-for-me-is-reading-other-peoples-code&quot;&gt;Those are good ideas. But what has worked best for me is reading other people’s code.&lt;/h2&gt;

&lt;p&gt;In fact, &lt;a href=&quot;/2022/12/18/LessonsFromExCoworkers/&quot;&gt;an ex-coworker taught me that lesson&lt;/a&gt;. That was his secret technique, even though he was our team’s architect, and we thought he didn’t need to study anymore.&lt;/p&gt;

&lt;p&gt;So read more code. But read actively. You can’t read code like fiction, glancing over words on a page:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Find a medium-sized library you use or find interesting&lt;/li&gt;
  &lt;li&gt;Download its source code&lt;/li&gt;
  &lt;li&gt;Compile and run it&lt;/li&gt;
  &lt;li&gt;Look at its unit tests&lt;/li&gt;
  &lt;li&gt;Debug one feature&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See how the library authors implemented a feature.&lt;/p&gt;

&lt;p&gt;If you find a new keyword, look it up. If you find a new data structure, look it up too. If you find a new method in the standard library, again, look it up.&lt;/p&gt;

&lt;p&gt;Once you understand how a feature is implemented, try to recreate a bare-bones version without looking at the original code. That’s how you make sure you truly understand the code.&lt;/p&gt;

&lt;p&gt;I used this strategy when I learned about the &lt;a href=&quot;/2024/09/30/SpecificationPattern/&quot;&gt;Specification pattern&lt;/a&gt;. I found a library implementing that pattern. I downloaded and read it. Once I got the main idea, I wrote my own dummy implementation. Then I wrote a post about it.&lt;/p&gt;

&lt;p&gt;Reading code has been one of the most effective ways to improve my coding skills. That’s why I made it one of the 30 proven strategies in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the roadmap I wish I had when I was starting out.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=best-way-get-better-coding-isnt-writing-code&quot;&gt;Grab your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Here&apos;s the Best Productivity Hack I Learned in 2024</title>
   <link href="https://canro91.github.io/2024/12/30/BestProductivityHack/"/>
   <updated>2024-12-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/30/BestProductivityHack</id>
   <content type="html">&lt;p&gt;It wasn’t a time management technique. It wasn’t the Eisenhower Matrix, Eat That Frog, or the Pareto Principle.&lt;/p&gt;

&lt;p&gt;It was an energy management technique: eating better.&lt;/p&gt;

&lt;h2 id=&quot;eating-better-eating-for-energy&quot;&gt;Eating better. Eating for energy.&lt;/h2&gt;

&lt;p&gt;Eating better means no processed foods, no carbs, and no sugar. We all know we should eat better. Nothing new! Right?!&lt;/p&gt;

&lt;p&gt;But from “Glucose Revolution” by Jessie Inchauspé, eating better means reordering how we eat, without sacrificing those delicious candies.&lt;/p&gt;

&lt;p&gt;Salads first. Proteins and fats second. And carbs and sweets last.&lt;/p&gt;

&lt;h2 id=&quot;reordering-how-we-eat-keeps-our-glucose-spikes-under-control&quot;&gt;Reordering how we eat keeps our glucose spikes under control&lt;/h2&gt;

&lt;p&gt;Glucose spikes are the reason why we feel tired all day and hungrier even after eating. We have too much sugar in our bloodstream.&lt;/p&gt;

&lt;p&gt;Simply by reordering how I ate, I got my energy back:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;I don’t get sleepy after lunch.&lt;/li&gt;
  &lt;li&gt;I don’t drag my feet to get through my afternoons.&lt;/li&gt;
  &lt;li&gt;I lost ~4kg in two months without much effort.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, to control our glucose spikes, we could walk for 10-15 minutes after eating. The longer, the better.&lt;/p&gt;

&lt;p&gt;Thanks to my new diet, my afternoons are as productive as my mornings. No more feeling like a zombie at the end of the day.&lt;/p&gt;

&lt;p&gt;We are what we put in our bodies. And if we eat well, we think well.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Best Books I Read in 2024 (Even If I Didn&apos;t Read Many)</title>
   <link href="https://canro91.github.io/2024/12/29/BestBooksOf2024/"/>
   <updated>2024-12-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/29/BestBooksOf2024</id>
   <content type="html">&lt;p&gt;In 2024, I stopped being a serial book reader, keeping a tally of books I read.&lt;/p&gt;

&lt;p&gt;I went from “let’s read anything and everything” to “let’s read what I need to read.” From just-in-case to just-in-time reading. I realized I had a long list of book notes, but I didn’t remember reading some of those books.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/11/29/ReadingMore/&quot;&gt;Naval Ravikant’s reading strategy&lt;/a&gt; helped me change my mind about books. And I adopted some reading methods into &lt;a href=&quot;/2024/05/13/HowToReadNonFictionBooks/&quot;&gt;my own reading method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead of reading a long list of books, I immersed myself in a single writer’s world.&lt;/p&gt;

&lt;p&gt;By accident, I found &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher’s work&lt;/a&gt; and I’ve been following him since then. I reread “Reinvent Yourself” and read “Choose Yourself.” Those are the best ones I’ve read.&lt;/p&gt;

&lt;h2 id=&quot;reinvent-yourself&quot;&gt;Reinvent Yourself&lt;/h2&gt;

&lt;p&gt;This was one of those books I didn’t remember reading, even when I had notes. Later, listening to James Altucher’s interviews, I made the connection and reread it.&lt;/p&gt;

&lt;p&gt;We can’t rely on the same skills we learned 5 years ago. We need to reinvent ourselves every 5 years. And here, reinvention means finding new income sources.&lt;/p&gt;

&lt;p&gt;To start a reinvention, we should &lt;a href=&quot;/2024/11/20/Read500Books/&quot;&gt;read 500 books on any subject&lt;/a&gt;. That would take us around 5 years.&lt;/p&gt;

&lt;h2 id=&quot;choose-yourself&quot;&gt;Choose Yourself&lt;/h2&gt;

&lt;p&gt;This is &lt;em&gt;the&lt;/em&gt; best book I read this year.&lt;/p&gt;

&lt;p&gt;Often, we’re waiting for someone to save us or choose us. Someone will see how smart we are and give us a job. Someone will see how attractive we are and choose us as life partners…The government, our boss, or a rich guy that doesn’t know what to do with his money. One day, someone will save us.&lt;/p&gt;

&lt;p&gt;The truth is no one is coming to rescue us. We should choose ourselves.&lt;/p&gt;

&lt;p&gt;We start choosing ourselves by taking care of our body, mind, and spirit every day: moving our bodies, doing something creative, and &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;writing 10 ideas a day&lt;/a&gt;. Working on things we love and being surrounded by people we love.&lt;/p&gt;

&lt;p&gt;And we don’t need anyone’s permission for that. The internet has created a world without permissions. The only permission we need is from ourselves.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Apart from these two, I read &lt;a href=&quot;/2025/01/19/ChooseYourselfGuideToWealth/&quot;&gt;Choose Yourself Guide to Wealth&lt;/a&gt;, and I have on my pile to read “The Power of No” and “Skip the Line.” All of them by James Altucher. Yes, I’m immersed in James Altucher’s work these days.&lt;/p&gt;

&lt;p&gt;Some honorable mentions: this year, I also read “E-Myth Revisited,” “You Are a Writer,” “Slow Productivity,” and “The Anxious Generation.”&lt;/p&gt;

&lt;p&gt;I can’t say one book has changed my life. But, those two books gave enough ideas and inspiration to take care of health and get life on track after a season of burned out.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>One Technique to Ease Your Onboarding—and Not Make New Team Members Feel Lost</title>
   <link href="https://canro91.github.io/2024/12/28/BetterOnboardings/"/>
   <updated>2024-12-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/28/BetterOnboardings</id>
   <content type="html">&lt;p&gt;The first moments show how you’re going to be treated for the rest of the time.&lt;/p&gt;

&lt;p&gt;It applies when you go to a restaurant, call a customer service line, or go to a bank. And the same is true when joining a new team as a developer.&lt;/p&gt;

&lt;p&gt;The first day I joined my last full-time job was awful.&lt;/p&gt;

&lt;p&gt;I was &lt;a href=&quot;/2024/10/14/LearningLanguages/&quot;&gt;speaking a foreign language for work&lt;/a&gt; for the first time. I was &lt;a href=&quot;/2020/08/08/LessonsOnRemoteWork/&quot;&gt;working from home&lt;/a&gt; for the first time too. My company laptop didn’t arrive that day. I was told to install the software project with long and outdated instructions. I was completely lost.&lt;/p&gt;

&lt;p&gt;I got a call from my boss’s boss that day, promising I’d “be in trouble” if I didn’t install that project before the end of the day.&lt;/p&gt;

&lt;p&gt;The funny thing is, I didn’t have to work with that project at all in the 5 years I was there.&lt;/p&gt;

&lt;h2 id=&quot;instead-of-making-your-new-team-members-feel-lost-set-clear-goals-for-the-first-day-week-and-month&quot;&gt;Instead of making your new team members feel lost, set clear goals for the first day, week, and month.&lt;/h2&gt;

&lt;p&gt;For the first day, a new team member should:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Install the right tools: email, VPN, chat clients, etc&lt;/li&gt;
  &lt;li&gt;Introduce themselves in Slack or company chat&lt;/li&gt;
  &lt;li&gt;Get to know their team lead&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the first week:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Have 1-1s with all team members&lt;/li&gt;
  &lt;li&gt;Have the working environment ready&lt;/li&gt;
  &lt;li&gt;Complete a dummy task to understand the project workflow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the first month:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Complete a medium-sized task, with almost no guidance&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Make it really easy for new team members to install your project and its dependencies. They should be ready to work by running a single script or pressing a button.&lt;/p&gt;

&lt;p&gt;Make it really easy for new team members to follow existing rules and conventions. Don’t rely on outdated documents. Show sample code or work that follows those conventions.&lt;/p&gt;

&lt;p&gt;Get your developers “up and running” as quickly as possible. Show them how you’re treating them for the rest of the time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>An Easy (And Clever) Way to Write a Non-Fiction Book</title>
   <link href="https://canro91.github.io/2024/12/27/WritingABook/"/>
   <updated>2024-12-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/27/WritingABook</id>
   <content type="html">&lt;p&gt;Writing a book is a sign of prestige and a synonym of expertise.&lt;/p&gt;

&lt;p&gt;A book brings interviews, talks, consulting gigs, and more opportunities. Sales don’t bring the most money, but the doors it opens do. That’s what I’ve heard.&lt;/p&gt;

&lt;p&gt;If you think of publishers, pitching, and rejection when you think of publishing a book, there’s the self-publishing route. Amazon, Gumroad, or your own site are good options.&lt;/p&gt;

&lt;p&gt;Writing a book can seem daunting.&lt;/p&gt;

&lt;p&gt;But in an interview for &lt;a href=&quot;https://www.youtube.com/watch?v=AtH1yR8pQpE&quot;&gt;Self Publishing TV on YouTube&lt;/a&gt;, James Altucher shared a simpler way to write a non-fiction book:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Find a topic you’re interested in.&lt;/li&gt;
  &lt;li&gt;Look up 10 scientific papers about it.&lt;/li&gt;
  &lt;li&gt;Explain each paper in simple words and share a story.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Et voilà! You have a book: “10 Scientifically Proven Ways to …”&lt;/p&gt;

&lt;p&gt;The thing is, scientific papers aren’t written for normal people. They’re impossible to read. Full of technical jargon and long paragraphs. Arrggg! I tried it when I wanted to understand &lt;a href=&quot;/2024/12/13/KeepingPhonesAround/&quot;&gt;phones reducing our cognitive capacities&lt;/a&gt;. I said, why they write like this?&lt;/p&gt;

&lt;p&gt;Even if you don’t write a book, this is a great idea for a newsletter, series of posts, or email course.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Surprising Lesson from My First Online Writing Class</title>
   <link href="https://canro91.github.io/2024/12/26/WriteDaily/"/>
   <updated>2024-12-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/26/WriteDaily</id>
   <content type="html">&lt;p&gt;This year, and for the first time since University, I took a writing class for the Internet.&lt;/p&gt;

&lt;p&gt;It was a webinar, to be precise. Tim Denning and Todd Brison ran it.&lt;/p&gt;

&lt;p&gt;The main lesson? Write daily. Daily?! Yes, daily.&lt;/p&gt;

&lt;p&gt;That was shocking. I was used to writing on my blog when I felt I had something to say. Usually, once a month. Later, when I gained some traction, twice a month. I only wrote daily when I challenged myself to &lt;a href=&quot;/AdventOfCode2022&quot;&gt;write ~20 posts before Christmas in 2022&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To write daily, you don’t have to produce 1,000-word posts each time. A good headline and one main point are enough to hit “Publish.” Even a tweet, a quote, or 200 words are a good place to start too.&lt;/p&gt;

&lt;p&gt;When you write daily:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You make people notice you and remember you by consistently showing up in their feeds.&lt;/li&gt;
  &lt;li&gt;You get closer to your future customers, bosses, and partners. Remember, people don’t buy from or do business with random people on the streets, but from people they know.&lt;/li&gt;
  &lt;li&gt;You build a library of content that showcases your expertise. People will see you as an expert, or at least someone who knows about the subject you’re writing about.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Write your first piece to get ahead of those who never start, and keep writing daily to get ahead of those who quit along the way. Your future self will thank you for it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We Shouldn&apos;t Call Them Best Practices—And Blindly Follow Them</title>
   <link href="https://canro91.github.io/2024/12/25/BestPractices/"/>
   <updated>2024-12-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/25/BestPractices</id>
   <content type="html">&lt;p&gt;We, as coders, take pride in preaching and following best practices.&lt;/p&gt;

&lt;p&gt;Don’t write SQL, use an ORM. Don’t write conditionals, use design patterns. Don’t throw exceptions, use Results…Don’t do that, do this.&lt;/p&gt;

&lt;p&gt;Those “don’t do that, do this” hide all the context in which they make sense. That’s the part we skip and don’t tell when we preach best practices.&lt;/p&gt;

&lt;p&gt;Today, I had a call with a consulting company that needed help. They were migrating a small shop’s application from the early 2000s to a newer stack. It wasn’t written and maintained by professional software engineers. Zero best practices. Lots of copy-pasting.&lt;/p&gt;

&lt;p&gt;Migrating that application and bringing its owners up to speed are two different challenges. They have to maintain the application once the migration is done. Using the latest and greatest best practices wasn’t an option.&lt;/p&gt;

&lt;p&gt;Often, instead of going all in on best practices, the best path to follow is &lt;em&gt;“let’s do the simplest thing that can work, without doing any more harm.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We shouldn’t call them &lt;em&gt;“best practices,”&lt;/em&gt; but rather &lt;em&gt;“pieces of advice that worked for me under certain circumstances and might work for you too.”&lt;/em&gt; And we shouldn’t blindly follow them. Not all code is created equal and worth the same.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Don&apos;t Write to Seem Smart, Write Like This Instead</title>
   <link href="https://canro91.github.io/2024/12/24/WritingVoice/"/>
   <updated>2024-12-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/24/WritingVoice</id>
   <content type="html">&lt;p&gt;Don’t write to seem smart or to sound like a famous writer.&lt;/p&gt;

&lt;p&gt;That will only make you use technical jargon, complicated words, and long boring paragraphs that scare people away.&lt;/p&gt;

&lt;h2 id=&quot;instead-write-for-only-one-person&quot;&gt;Instead, write for only one person.&lt;/h2&gt;

&lt;p&gt;Every time you sit to write, imagine you’re writing for a friend, coworker, or your past self.&lt;/p&gt;

&lt;p&gt;It will give you the right tone, context, and level of detail. You won’t use words or jargon you wouldn’t use in a real conversation. Instead, you will use your true voice.&lt;/p&gt;

&lt;p&gt;I wrote most of my coding tutorials for a friend or two. They almost never asked me to write them. I never showed them the finished piece. I simply imagined myself explaining something to them to make my writing easier.&lt;/p&gt;

&lt;p&gt;If writing for someone sounds hard, record yourself explaining something and transcribe it.&lt;/p&gt;

&lt;p&gt;What’s the point of sounding smart if your writing get so complicated that people don’t read what you write?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Write with the same voice you talk in. You’ve spent your whole life learning how to communicate with that voice. Why change it when you communicate with text?”&lt;/em&gt; — James Altucher&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>One Language Trick I Use to Ace Interviews in a Second Language</title>
   <link href="https://canro91.github.io/2024/12/23/InterviewsInForeignLanguages/"/>
   <updated>2024-12-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/23/InterviewsInForeignLanguages</id>
   <content type="html">&lt;p&gt;Interviews are intimidating.&lt;/p&gt;

&lt;p&gt;You don’t know what you’re going to be asked. You don’t know if the interviewer will like you. You have to remember what you wrote on your CV.&lt;/p&gt;

&lt;p&gt;That makes interviews intimidating for sure. But the ones in a second language are worse.&lt;/p&gt;

&lt;p&gt;Are my speaking skills good enough? What if I don’t understand my interviewer’s accent? What if I forget how to say something? Arrggg! I know that feeling.&lt;/p&gt;

&lt;p&gt;When I decided to apply to my last full-time job as a software engineer, I really wanted to land the job. I was about to work remotely for American companies for the first time. I had to impress my interviewers not only with my coding skills, but with my language skills too.&lt;/p&gt;

&lt;p&gt;Right before the interviews, to calm my nerves and practice my speaking skills, I phoned my language partner and friend. She was kind enough to answer my calls and help me out. We talked about anything.&lt;/p&gt;

&lt;p&gt;Those calls helped me switch to thinking in a second language, like pushing a button in my brain.&lt;/p&gt;

&lt;p&gt;Eventually, I stopped calling my friend before every interview. But I adapted the method.&lt;/p&gt;

&lt;p&gt;Instead of interrupting my friend’s busy schedule, now I watch a short YouTube video of a movie or TV show or any content by native speakers right before an interview in a second language. It gets me into the flow of the language and makes my brain switch the second language on.&lt;/p&gt;

&lt;p&gt;And that’s one of the tricks I used for interviews.&lt;/p&gt;

&lt;p&gt;As a bonus, here are others:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Keep a copy of your CV next to you.&lt;/li&gt;
  &lt;li&gt;Learn conversation fillers to keep the conversation going.&lt;/li&gt;
  &lt;li&gt;Practice answering out loud the most common interview questions. At least be prepared for: “tell me about yourself” and “could you describe one of your past projects?”&lt;/li&gt;
  &lt;li&gt;Speak slowly. It will give you time to think.&lt;/li&gt;
  &lt;li&gt;If you don’t understand a question, restate what you understood and ask if that’s what the interviewer meant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interviewing and hiring are broken. I know. But don’t go unprepared. Prime your brain by listening to or watching native content before showing up.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>3 Lessons I Learned from Watching Two Millionaires Talk About Money</title>
   <link href="https://canro91.github.io/2024/12/22/MillionairesTalking/"/>
   <updated>2024-12-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/22/MillionairesTalking</id>
   <content type="html">&lt;p&gt;Having too much money brings a different set of challenges.&lt;/p&gt;

&lt;p&gt;That’s one of the takeaways from this conversation between two millionaires. It’s one episode of the “Deep Dive with Ali Abdaal.” This time Ali sat down with Andrew Wilkinson.&lt;/p&gt;

&lt;p&gt;Here’s the link to the &lt;a href=&quot;https://www.youtube.com/watch?v=VPJYBnmxNMI&quot;&gt;YouTube episode&lt;/a&gt; if you want to watch it.&lt;/p&gt;

&lt;p&gt;Ali Abdaal started medical school, then became a YouTuber, and since then has run an online education business. But I didn’t know about Andrew Wilkinson. Andrew went from barista to billionaire, coming from a middle-class family.&lt;/p&gt;

&lt;h2 id=&quot;im-as-stressed-as-you-are&quot;&gt;“I’m as Stressed as You Are”&lt;/h2&gt;

&lt;p&gt;For me, hearing Andrew say he was stressed too was one of the most shocking parts of that conversation — right at the start.&lt;/p&gt;

&lt;p&gt;We might believe that money makes life easier. But from that conversation: it just brings a new set of challenges. We have to choose our own money adventure and when to stop.&lt;/p&gt;

&lt;p&gt;This reminds me of a past boss.&lt;/p&gt;

&lt;p&gt;He was a well-known entrepreneur in my city (and maybe in my whole country). He came from a wealthy family and ran more than a couple of successful businesses. But when he tasted the bat soup in 2020, there was no money to add more time to his countdown timer.&lt;/p&gt;

&lt;p&gt;I guess money can’t buy certain things. For everything else, there’s a credit card. I’m stealing that from a TV commercial.&lt;/p&gt;

&lt;h2 id=&quot;be-a-financial-prepper&quot;&gt;“Be a Financial Prepper”&lt;/h2&gt;

&lt;p&gt;Be paranoid. Only the paranoids survive.&lt;/p&gt;

&lt;p&gt;During the conversation, Andrew shared he’s a prepper. Not in the sense of keeping a basement full of weapons, canned food, and gas masks ready for a zombie apocalypse. But in the sense of having multiple income sources.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Be unbreakable financially”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In 2024, I had to internalize that lesson thanks to yet another round of layoffs in the software industry. I lost my main income source, a.k.a salary. I had other income sources, but not enough to cover my monthly expenses.&lt;/p&gt;

&lt;p&gt;Robert Kiyosaki is right: “Build your asset column” and “Make your assets pay for your luxuries.”&lt;/p&gt;

&lt;p&gt;I had to truly live it to learn it. Knowledge is only potential power unless put into practice.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The best way to feel rich is to have cashflow”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;be-the-sushi-master-or-the-chipotle-founder&quot;&gt;Be the Sushi Master or the Chipotle Founder&lt;/h2&gt;

&lt;p&gt;Apart from the money lessons, my most important takeaway from this podcast episode is to:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Design your life around your flow state”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could choose to be the best sushi master of the world — worth a Netflix documentary — or the Chipotle founder.&lt;/p&gt;

&lt;p&gt;One has mastered his craft to the point of perfection. Maybe he’s happy having only one restaurant and serving one smallish crowd. The other took a different route and created a reproducible business with thousands of locations, taking himself out of the equation.&lt;/p&gt;

&lt;p&gt;That’s success seen from different perspectives.&lt;/p&gt;

&lt;p&gt;Both of them followed what they enjoyed doing. Each followed a different adventure.&lt;/p&gt;

&lt;p&gt;It sounds like the story of a businessman who ran into a fisherman taking a nap in a hammock under a palm tree. After a long conversation and giving a business plan for free, the businessman realizes that the entrepreneurial journey he was sharing with the fisherman will end with a nap in a hammock under the same palm tree.&lt;/p&gt;

&lt;p&gt;In any case, follow your flow state and delegate things you don’t enjoy.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Towards the end of the conversation, Ali started to ask for advice to expand his own business. It was interesting to hear the business and money insights from a billionaire who came from the middle class. It wasn’t advice for everyone, but I took this last part:&lt;/p&gt;

&lt;p&gt;“Sell something boring to a rich person. Don’t sell a complex product to cheap people.”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How A Salary Messed With My Mind—Two Realizations After a Layoff</title>
   <link href="https://canro91.github.io/2024/12/21/MessySalary/"/>
   <updated>2024-12-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/21/MessySalary</id>
   <content type="html">&lt;p&gt;“Wait, I’m not working anymore.”&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2024/12/05/HowALayoffFeels/&quot;&gt;Being laid off feels weird&lt;/a&gt;. Moments of relief followed by a “What am I going to do now?”&lt;/p&gt;

&lt;p&gt;Some days after being laid off last January, something weird happened. I didn’t realize I had ingrained this “habit.”&lt;/p&gt;

&lt;p&gt;Midway through rushing to my laptop to reply to my Teams messages, I suddenly stopped my short commute from the living room to my working corner.&lt;/p&gt;

&lt;p&gt;“Wait a second, I’m not working anymore…I don’t have to reply to messages or emails.” Pheeew!&lt;/p&gt;

&lt;p&gt;I shared this realization with my family. We all laughed. But there was something behind it.&lt;/p&gt;

&lt;p&gt;Stopping my short breaks to reply to messages had become second nature. I was trapped in a mindset. In the wrong mindset. “I could get into trouble if I don’t respond soon.”&lt;/p&gt;

&lt;p&gt;That moment of relief lasted a few days. Anxiety knocked at my door: “Somebody here?” I realized I wasn’t receiving another paycheck. Arrrggg! “What am I going to do now?”&lt;/p&gt;

&lt;p&gt;I had to go through my accounting software, an Excel spreadsheet, multiple times to make the anxiety go away. “Rational brain help me out here.” I had an emergency fund to cover my expenses for a few months.&lt;/p&gt;

&lt;p&gt;Yes, a monthly salary is one of three most harmful addictions. I had already heard that quote. This time I had to live it.&lt;/p&gt;

&lt;p&gt;That was a moment of relief followed by a “What am I going to do now?”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Following This Plan To Learn Enough Copywriting To Be Dangerous</title>
   <link href="https://canro91.github.io/2024/12/20/CopywritingStudyPlan/"/>
   <updated>2024-12-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/20/CopywritingStudyPlan</id>
   <content type="html">&lt;p&gt;Writing is the superpower to survive in the online world.&lt;/p&gt;

&lt;p&gt;All online content starts with writing. A video begins with a script, a course with a series of posts, and a long post with a Tweet or short-form post.&lt;/p&gt;

&lt;p&gt;And guess what? Marketing and sales start with writing, too. A landing page, an email sequence, a product description, and an ad.&lt;/p&gt;

&lt;p&gt;That’s when copywriting comes into the picture. Copywriting means writing to make readers take action: like and subscribe, download a freebie, and ultimately buy.&lt;/p&gt;

&lt;p&gt;This is my 3-step plan to learn enough copywriting to be safely dangerous.&lt;/p&gt;

&lt;h2 id=&quot;1-start-by-writing-classics-by-hand&quot;&gt;1. Start by writing classics by hand&lt;/h2&gt;

&lt;p&gt;The #1 exercise I have found to learn to copywrite is &lt;strong&gt;copywork&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Copywork means recreating classic ads, sales letters, and good “copy” by hand. Yes, by hand. That’s the whole point. When we handwrite, we’re forced to slow down, read carefully, and pay attention to words and phrases.&lt;/p&gt;

&lt;p&gt;I’m not new to this exercise. When I started writing coding tutorials, I copied &lt;a href=&quot;https://seths.blog/&quot;&gt;Seth Godin’s posts&lt;/a&gt;. I noticed how he doesn’t use introductions or conclusions in his posts. Instead, the first sentence naturally follows the post title.&lt;/p&gt;

&lt;p&gt;The goal of copywork isn’t to transcribe. It’s about noticing the structure, common phrases, and other patterns.&lt;/p&gt;

&lt;p&gt;These are 3 posts where you can find samples to copywork:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://copywritingcourse.com/blogs/106-copywork/&quot;&gt;Get Better At Copywriting By Handwriting Famous Pieces Of Work!&lt;/a&gt; —I’ve been following this post this month. That’s one example per day for 31 days.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://officialsarahturner.com/blog/copywriting-examples/&quot;&gt;5 Ad Copywriting Examples to Study, Hand Copy, and Save to Your Copywriting Swipe File&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://carminemastropierro.com/gary-halbert-copywriting-examples/&quot;&gt;Gary Halbert Copywriting Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I bought a notebook for my copywork and color pens. After I finish transcribing a copy, I use a pen with one color to notice words and phrases. Then, at the margins of the page, I use another pen to notice the overall structure and formulas.&lt;/p&gt;

&lt;p&gt;And there’s one online course built around the idea of copywork: &lt;a href=&quot;https://lp.copyhour.com/&quot;&gt;CopyHour&lt;/a&gt;. That’s 90 days of daily copywork—Even we can handwrite that landing page!&lt;/p&gt;

&lt;p&gt;Everywhere I looked, I found references to this course. Definitely, I’m keeping an eye on the next enrollment.&lt;/p&gt;

&lt;h2 id=&quot;2-read-the-boron-letters&quot;&gt;2. Read the Boron Letters&lt;/h2&gt;

&lt;p&gt;Gary Halbert is one name that keeps popping up in any Google search about copywriting.&lt;/p&gt;

&lt;p&gt;He is a legend in the copywriting world. He wrote sales letters and ads for well-known brands, making millions of dollars in sales. Think of him like Hemingway going the sales way.&lt;/p&gt;

&lt;p&gt;The thing is, Gary Halbert ended up in jail. From there, he wrote a series of letters to his son, teaching him to make money through copywriting. Those letters became a book: &lt;strong&gt;The Boron Letters&lt;/strong&gt;—named after the Boron Federal Penitentiary where Gary was.&lt;/p&gt;

&lt;p&gt;After doing copywork, my next step is reading and studying The Boron Letters.&lt;/p&gt;

&lt;p&gt;I picked up The Boron Letters years ago but didn’t finish it. At that time, I thought it was another writing book. I didn’t understand what copywriting was and its goal, and dropped it. I wasn’t ready yet.&lt;/p&gt;

&lt;p&gt;All that I remember is the advice from someone in jail to eat a banana a day, exercise, and grow big arms because bullies don’t mess with people with big arms. That’s from the first two letters, by the way.&lt;/p&gt;

&lt;p&gt;Since reading three books will put us ahead of 90% of people, here are another two copywriting books: “Writing That Works” and “The Adweek Copywriting Handbook.” Those books are on my to-read list after The Boron Letters.&lt;/p&gt;

&lt;h2 id=&quot;3-revisit-my-landing-pages-and-calls-to-action&quot;&gt;3. Revisit my landing pages and calls to action&lt;/h2&gt;

&lt;p&gt;This is the dangerous part. Knowledge is only potential power unless it’s put into practice.&lt;/p&gt;

&lt;p&gt;I don’t have that many sales pages yet, but a couple of landing pages for coding courses and freebies in my Gumroad account. I want to revisit those landing pages and the calls to action at the end of my posts.&lt;/p&gt;

&lt;p&gt;Originally, I wrote them following what I saw online: &lt;em&gt;“Click here”&lt;/em&gt; and &lt;em&gt;“Join my course here.”&lt;/em&gt; I had no idea about copywriting and frameworks like Problem/Agitation/Solution.&lt;/p&gt;

&lt;p&gt;One trick I’ve learned is to write in terms of benefits, not features. Always write with the reader in mind. That’s the difference between &lt;em&gt;“1GB of storage”&lt;/em&gt; vs &lt;em&gt;“carry 100 songs in your pockets wherever you go.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that trick in mind, I changed one of my CTAs from a simple &lt;em&gt;“Join my free 7-day email course here”&lt;/em&gt; to something along the lines of &lt;em&gt;“Join my free 7-day email course and save years and thousands of dollars’ worth of career mistakes.”&lt;/em&gt; And I got my first two subscribers after that change.&lt;/p&gt;

&lt;p&gt;If there’s one skill you can master for the future, choose writing online. And don’t worry about ChatGPT or Copilot because they will always output average writing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stop Time Traveling—Yes, You&apos;re Already Doing It</title>
   <link href="https://canro91.github.io/2024/12/19/TimeTravel/"/>
   <updated>2024-12-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/19/TimeTravel</id>
   <content type="html">&lt;p&gt;You don’t need to speed a DeLorean to 88mph to time travel. In fact, you’re already doing it.&lt;/p&gt;

&lt;p&gt;Every time you go into your head to relive past situations, you’re time traveling. And every time you go into your head to anticipate future situations, you’re time traveling too.&lt;/p&gt;

&lt;p&gt;I declare myself guilty of time traveling.&lt;/p&gt;

&lt;p&gt;I’ve time traveled to the day when the ex-bosses “who let me go” finally realize how talented and smart I am and beg me to return. That probably will never happen. And I’ve only wasted time and mental energy time traveling.&lt;/p&gt;

&lt;p&gt;By reading Choose Yourself by James Altucher, I learned to have a “Daily Practice:” A routine to work on my physical, mental, emotional, and spiritual self every day. We need to work on all of them to be healthy.&lt;/p&gt;

&lt;p&gt;As part of the Daily Practice, &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt; recommends stopping time traveling.&lt;/p&gt;

&lt;p&gt;For some people, it means having moments of silence during the day to breathe and notice their thoughts.&lt;/p&gt;

&lt;p&gt;I won’t call it “meditation.” Probably, when you hear that word, the first thing that comes to mind is a Buddhist monk sitting in impossible poses chanting. But it’s simpler than that.&lt;/p&gt;

&lt;p&gt;When you notice you’re time traveling, repeat to yourself: “Stop, breathe, and get out of your mind.”&lt;/p&gt;

&lt;p&gt;Time traveling only brings resentment. Stop doing it and get back to the present.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>You&apos;re an Alarm Away from Being Healthier, Wealthier, and Happier</title>
   <link href="https://canro91.github.io/2024/12/18/3Alarms/"/>
   <updated>2024-12-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/18/3Alarms</id>
   <content type="html">&lt;p&gt;Phones are not just communication devices. They’re distraction machines with constant beeps and buzzes.&lt;/p&gt;

&lt;p&gt;Keeping our phones around reduces our cognitive abilities, even when we’re not using them. I learned this from reading “The Anxious Generation,” by the way.&lt;/p&gt;

&lt;p&gt;But that doesn’t mean we can’t use those beeps and buzzes to our advantage. They can be reminders of what we want to accomplish.&lt;/p&gt;

&lt;p&gt;I have an alarm to unplug from work labeled “It’s time to relax.” During that time, I do anything I want outside work without feeling guilty. It’s one of the few sounds I allow my phone to make. All others are deactivated.&lt;/p&gt;

&lt;p&gt;From reading &lt;a href=&quot;https://ericpartaker.com/the-3-alarms&quot;&gt;The 3 Alarms by Eric Partaker&lt;/a&gt;, I learned to use alarms not only to schedule my downtime but also to improve the three main areas of our lives: health, work, and relationships.&lt;/p&gt;

&lt;h2 id=&quot;heres-how&quot;&gt;Here’s how:&lt;/h2&gt;

&lt;p&gt;Start by setting 3 alarms on your phone.&lt;/p&gt;

&lt;p&gt;Label each alarm with a reminder of who you want to become: the healthy parent, the focused solopreneur, or the supportive partner. You name it!&lt;/p&gt;

&lt;p&gt;When the alarm rings, do something that takes you closer to that. It doesn’t have to be a huge action, just a small step.&lt;/p&gt;

&lt;p&gt;When “the healthy parent” rings, get up and move your body. When “the focused solopreneur” rings, put all distractions away and work on your side business. When “the supportive partner” rings, call or text your partner.&lt;/p&gt;

&lt;p&gt;Those small steps will create habits, and habits will reinforce who you want to become.&lt;/p&gt;

&lt;p&gt;You can set an alarm to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Exercise&lt;/li&gt;
  &lt;li&gt;Drink water&lt;/li&gt;
  &lt;li&gt;Read a book&lt;/li&gt;
  &lt;li&gt;Stretch your body&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;Write down 10 ideas&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Write a gratitude list&lt;/li&gt;
  &lt;li&gt;Step away from screens&lt;/li&gt;
  &lt;li&gt;Have a moment of silence&lt;/li&gt;
  &lt;li&gt;Work on your side business&lt;/li&gt;
  &lt;li&gt;Call or text someone you love&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And label that alarm as a reminder of the person you want to become.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Followed This One Trick to Write Better Headlines. Here&apos;s What I Found</title>
   <link href="https://canro91.github.io/2024/12/17/BetterHeadlines/"/>
   <updated>2024-12-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/17/BetterHeadlines</id>
   <content type="html">&lt;p&gt;“Five times as many people read the headline as read the body.”&lt;/p&gt;

&lt;p&gt;That’s a quote from David Ogilvy, “the father of advertising.” It applies to the copywriting world, as well as other forms of writing.&lt;/p&gt;

&lt;p&gt;A good headline is like the welcome sign for the rest of your piece. People passing by will choose to read only after seeing a good headline. No matter how well written and insightful your content is, a poor headline will make people skip to the next piece in their feeds.&lt;/p&gt;

&lt;p&gt;Apart from writing 10 headlines a day, the best piece of advice I’ve found to write better headlines is to look at YouTube headlines.&lt;/p&gt;

&lt;p&gt;Following that advice, I sneaked into Ali Abdaal’s YouTube channel. Here are the headlines of 10 of his most popular videos:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;How I type REALLY fast (156 words per minute)&lt;/li&gt;
  &lt;li&gt;My evidence-based skincare routine&lt;/li&gt;
  &lt;li&gt;Why you’re always tired - 7 myths ruining your sleep&lt;/li&gt;
  &lt;li&gt;If I started a YouTube channel in 2025, I’d do this&lt;/li&gt;
  &lt;li&gt;The book that changed my financial life&lt;/li&gt;
  &lt;li&gt;What makes people successful?&lt;/li&gt;
  &lt;li&gt;How writing made me a millionaire&lt;/li&gt;
  &lt;li&gt;My honest advice to someone who wants financial freedom&lt;/li&gt;
  &lt;li&gt;My favorite note-taking app for students - Notion&lt;/li&gt;
  &lt;li&gt;The best book I’ve ever read about making money&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those headlines follow a pattern:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;How I “action”&lt;/li&gt;
  &lt;li&gt;My evidence-based “action”&lt;/li&gt;
  &lt;li&gt;“Question about pain” - n “promise”&lt;/li&gt;
  &lt;li&gt;If I “action”, I’d do this&lt;/li&gt;
  &lt;li&gt;The “resource” that “outcome”&lt;/li&gt;
  &lt;li&gt;How “action” made me “outcome”&lt;/li&gt;
  &lt;li&gt;My honest advice to “audience” who wants “result”&lt;/li&gt;
  &lt;li&gt;The best “resource” I’ve ever “action” about “subject”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s a good starting place to write better headlines.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>It&apos;s OK if You Don&apos;t Have a Single True Passion</title>
   <link href="https://canro91.github.io/2024/12/16/FindYourPassion/"/>
   <updated>2024-12-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/16/FindYourPassion</id>
   <content type="html">&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1674221525704-f4b2aa13df2c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxNTg4MTM4Nw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;a dance school&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@familyschaffner?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Astrid Schaffner&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/a-group-of-ballet-dancers-tying-their-shoes-VRqA3J78DpA?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;“Find your passion” has always stressed me out.&lt;/p&gt;

&lt;p&gt;That advice assumes we all have a single true passion we can find within ourselves.&lt;/p&gt;

&lt;p&gt;I’ve always had trouble finding that one true passion ever since high school. I got good grades in all subjects, with more effort in some than others. Picking one thing wasn’t easy based on grades alone.&lt;/p&gt;

&lt;p&gt;I struggled to choose what to study at university. I had many ideas: joining the Military, studying Biology, or taking the Engineering route.&lt;/p&gt;

&lt;p&gt;I’ve always had trouble finding one specific thing as a software engineer and solopreneur.&lt;/p&gt;

&lt;p&gt;All that stress started to fade away when I realized it’s okay not to have one single true passion.&lt;/p&gt;

&lt;h2 id=&quot;embrace-your-multiple-passions&quot;&gt;Embrace Your Multiple Passions&lt;/h2&gt;

&lt;p&gt;The first counter-advice to “find your passion” I found was by watching Emilie Wapnick’s TED talk “Why Some of Us Don’t Have One True Calling.”&lt;/p&gt;

&lt;p&gt;She shares her story of having multiple interests and struggling to answer “What do you do for a living?”&lt;/p&gt;

&lt;p&gt;This intrigued me to the point of devouring most of the articles on her website.&lt;/p&gt;

&lt;p&gt;The main lesson from Emilie’s work is to embrace our multiple interests and passions. She even coined a term for that: being a &lt;strong&gt;multipotentialite&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Based on the quiz on her site, I discovered I’m a mixed-multipotentialite: someone who balances two passions at a time instead of many unfinished projects. This realization helped me connect the dots.&lt;/p&gt;

&lt;p&gt;I bought the idea of embracing my interests. But my first question was, “How can multipotentialites make a living? Who needs or hires them?” All the advice I had heard was to find one passion, one thing, or one niche.&lt;/p&gt;

&lt;p&gt;Emilie surveyed her community and found four ways to balance work as a multipotentialite:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Einstein approach&lt;/strong&gt;: A “boring” or “good enough” job pays the bills while you have something after hours. Einstein worked at the Patents Office while he started his Physics work.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Slash approach&lt;/strong&gt;: A two-job career. I’m a “software engineer/writer.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Group Hug approach&lt;/strong&gt;: A multi-disciplinary job that allows you to combine your multiple passions or interests.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phoenix approach&lt;/strong&gt;: A seasonal approach, changing careers to follow one of your passions per season.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don’t run from your multiple interests. Embrace them. There are always options to make a living from your multiple interests.&lt;/p&gt;

&lt;h2 id=&quot;dont-be-an-engineer-be-a-producer&quot;&gt;Don’t Be an Engineer, Be a Producer&lt;/h2&gt;

&lt;p&gt;My second counter-advice to finding a single true passion came from the book “Range: Why Generalists Triumph in a Specialized World” by David Epstein.&lt;/p&gt;

&lt;p&gt;An ex-boss recommended it during one of our 1-on-1s. It’s also one of Bill Gates’ recommendations on his YouTube channel. Not that he’s one of my ex-bosses or that I had 1-on-1s with him.&lt;/p&gt;

&lt;p&gt;Gunpei Yokoi’s story is the one I remember the most from the book.&lt;/p&gt;

&lt;p&gt;He didn’t have a single interest. He worked as a maintenance guy for card-making machines at Nintendo back in the ’60s. He spent most of his first days there playing with the company equipment.&lt;/p&gt;

&lt;p&gt;One day, Nintendo’s president saw him playing with an extendable arm and asked him to turn it into a toy. The Nintendo Ultra Hand was born. It saved Nintendo from debt and started the Research and Development department at Nintendo.&lt;/p&gt;

&lt;p&gt;Looking for creative ways to repurpose old technology, Yokoi’s team was responsible for many of Nintendo’s successes over the years. The Game Boy was one of them. Yes, the Game Boy!&lt;/p&gt;

&lt;p&gt;When asked about his approach, Yokoi said &lt;em&gt;“I don’t have any particular specialist skills, I have a sort of vague knowledge about everything.”&lt;/em&gt; He had a range of skills.&lt;/p&gt;

&lt;p&gt;Yokoi taught his team to be producers, not engineers. An engineer focuses on the details. But a producer knows how to connect things.&lt;/p&gt;

&lt;p&gt;Instead of following a single interest, be a scientist who follow hypotheses, runs experiments, and tests and learns. Build a range of skills.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Be a flirt with your possible selves. Rather than a grand plan, find experiments that can be undertaken quickly.”-Range by David Epstein&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;

&lt;p&gt;Don’t worry about finding a single true passion and following it for the rest of your life.&lt;/p&gt;

&lt;p&gt;It’s OK to have multiple interests and juggle between them. It’s OK to change passions. It’s OK not to have one single niche. It’s OK to be a generalist.&lt;/p&gt;

&lt;p&gt;Having multiple passions brings a sense of curiosity and a desire to learn about the world around us.&lt;/p&gt;

&lt;p&gt;The world needs both focused frogs and visionary birds-deep-focused specialists and “above the trees” generalists who can see the bigger picture.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I Organize My Blogging Workflow</title>
   <link href="https://canro91.github.io/2024/12/15/BloggingWorkflow/"/>
   <updated>2024-12-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/15/BloggingWorkflow</id>
   <content type="html">&lt;p&gt;A schedule and an easy-to-follow workflow.&lt;/p&gt;

&lt;p&gt;That’s what you need if you want to keep blogging in the long run.&lt;/p&gt;

&lt;p&gt;I started &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;blogging back in 2018&lt;/a&gt;. I threw up some words in a file and put them online. No schedule or intentions. Only when I adopted a writing schedule, I started to improve my writing skills and notice more pageviews.&lt;/p&gt;

&lt;p&gt;To &lt;a href=&quot;/2024/11/09/LimitedKeystrokes/&quot;&gt;preserve my keystrokes&lt;/a&gt; and expand on my comment to &lt;a href=&quot;https://dev.to/olgabraginskaya/how-do-you-organize-your-blogging-workflow-18d4&quot;&gt;this dev.to question&lt;/a&gt; about blogging, here’s my writing workflow:&lt;/p&gt;

&lt;h2 id=&quot;one-platform-first-then-cross-post-everywhere&quot;&gt;One platform first then cross-post everywhere&lt;/h2&gt;

&lt;p&gt;Recently, I started to follow the POSSE principle: Publish (on your) Own Site, Syndicate Elsewhere.&lt;/p&gt;

&lt;p&gt;My blog is my main hub. Anything I write online, in some shape or form, ends up on my blog. Recently, I created a new tag &lt;a href=&quot;/tags/misc&quot;&gt;misc&lt;/a&gt; to organize my posts about other subjects apart from coding or software engineering.&lt;/p&gt;

&lt;p&gt;Based on the type of content: tutorial vs opinion piece, I republish a shorter version on &lt;a href=&quot;https://dev.to/canro91&quot;&gt;dev.to&lt;/a&gt; or republish on &lt;a href=&quot;https://medium.com/@iamcesaraguirre&quot;&gt;Medium&lt;/a&gt;, linking back to the original post.&lt;/p&gt;

&lt;p&gt;Since often some Medium publications only accept original content, I publish there first and then the next day, I publish on my blog. Win-win.&lt;/p&gt;

&lt;h2 id=&quot;raw-post-content-in-a-single-place&quot;&gt;Raw post content in a single place&lt;/h2&gt;

&lt;p&gt;I’m a &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;plain-tex fan&lt;/a&gt;. So, markdown everywhere.&lt;/p&gt;

&lt;p&gt;I keep my raw posts on a note-taking app in Markdown. Since I use GitHub with Jekyll to host my blog, publishing a new post is a simple commit and a push. Or when I’m feeling lazier than usual, I paste the post content directly on GitHub.&lt;/p&gt;

&lt;p&gt;For images, I host them on GitHub itself. I organize them inside a folder named “assets,” with subfolders named after my posts’ titles.&lt;/p&gt;

&lt;p&gt;For my most recent posts, I’ve ditched images and banners and started to &lt;a href=&quot;/2024/11/13/BlogMore/&quot;&gt;write shorter text-only posts&lt;/a&gt;. A decent headline and one main idea are good enough to publish.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;To republish on Medium: I use the import feature. It works fine, except for code listings. I copy and paste them and add some emojis to highlight important lines of code.&lt;/li&gt;
  &lt;li&gt;To republish on dev.to: I use a template with disclaimers, intros, and CTAs. Then I copy and paste that template and only fill in the post body in the editor itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to stay consistent and keep blogging, make it really easy. Reduce all the friction and automate as much as you can.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Follow This 1 Tip to Truly Stay Consistent With Any Skill</title>
   <link href="https://canro91.github.io/2024/12/14/Consistency/"/>
   <updated>2024-12-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/14/Consistency</id>
   <content type="html">&lt;p&gt;Want to master any skill? Simple!&lt;/p&gt;

&lt;p&gt;Show up. Consistency is king. Do something every day for one year.&lt;/p&gt;

&lt;p&gt;That’s common Internet advice to master a skill and crush your goals.&lt;/p&gt;

&lt;p&gt;Want to write? Show up.
Want to get in shape? Consistency is king.&lt;/p&gt;

&lt;p&gt;Easier said than done.&lt;/p&gt;

&lt;h2 id=&quot;show-up-is-good-advice-but-its-only-part-of-the-full-piece-of-advice&quot;&gt;Show up is good advice. But it’s only part of the full piece of advice&lt;/h2&gt;

&lt;p&gt;If you want to stay consistent, commit to the smallest action you can sustain in the long term.&lt;/p&gt;

&lt;p&gt;At the start of the year, I joined a monthly reading club with some friends. In the first sessions, everybody showed up with their reading done. We were so enthusiastic that we wanted to read more, have online discussions, and write essays with our reactions. We even wanted to meet up every weekend.&lt;/p&gt;

&lt;p&gt;But after a couple of sessions, somebody showed up without the reading done. And then, somebody else stopped showing up. And then somebody else…and somebody else. Eventually, we stopped our reading club.&lt;/p&gt;

&lt;p&gt;We bit off more than we could chew. And like any plan, it didn’t resist contact with reality.&lt;/p&gt;

&lt;h2 id=&quot;to-stay-consistent-show-up-but-with-small-actions&quot;&gt;To stay consistent, show up, but with small actions&lt;/h2&gt;

&lt;p&gt;Want to write? Commit to writing less than 200 words a day.
Want to get in shape? Commit to doing 1 push-up a day.
Want to read more? Commit to reading 1 page a day.&lt;/p&gt;

&lt;p&gt;And make it easy to finish your small action of the day.&lt;/p&gt;

&lt;p&gt;Leave your writing app open.
Go to bed wearing your workout clothes.
Keep a book on your desk.&lt;/p&gt;

&lt;p&gt;Small and easy-to-finish actions in the long run. That’s how you keep showing up.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Keeping Your Phone Around Reduces Your Cognitive Capacities</title>
   <link href="https://canro91.github.io/2024/12/13/KeepingPhonesAround/"/>
   <updated>2024-12-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/13/KeepingPhonesAround</id>
   <content type="html">&lt;p&gt;That was the result of &lt;a href=&quot;https://www.journals.uchicago.edu/doi/full/10.1086/691462&quot;&gt;a study&lt;/a&gt; by the University of Texas, cited in the book “The Anxious Generation.”&lt;/p&gt;

&lt;p&gt;In the study, they divided an undergraduate classroom into thirds.&lt;/p&gt;

&lt;p&gt;One third left their phones in another room. Another third kept them in their pockets or bags. And the last third kept them facing down on their desks.&lt;/p&gt;

&lt;p&gt;Guess who performed best at the end of some cognitive tests?&lt;/p&gt;

&lt;p&gt;Of course, the third that kept their phones outside did best. And the other two thirds did poorly, with some differences depending on the tests. But they claimed that their phone’s presence didn’t affect their performance.&lt;/p&gt;

&lt;p&gt;The mere presence of their phones reduced their cognitive abilities, even when they were silent or off.&lt;/p&gt;

&lt;p&gt;If you have read Deep Work, you already know about monk mode. But to focus, you don’t have to go into strict mode and retreat to a cabin without reception in the middle of nowhere. Just ask your phone to give you some space. Leave your phone in another room, out of sight.&lt;/p&gt;

&lt;p&gt;Now, while I’m typing this, my phone is in airplane mode, in another room.&lt;/p&gt;

&lt;p&gt;So if you want to do focused work, leave your phone out of sight.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Life-Changing Purchases I&apos;ve Made Since 2020</title>
   <link href="https://canro91.github.io/2024/12/12/LifeChangingPurchases/"/>
   <updated>2024-12-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/12/LifeChangingPurchases</id>
   <content type="html">&lt;p&gt;I’m answering this &lt;a href=&quot;https://news.ycombinator.com/item?id=42079768&quot;&gt;Hacker News question&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is there anything you’ve bought in the past few years (since 2020) that really changed something in your life?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They didn’t change my life, but they’ve made it somewhat better:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;A pair of 5-kg dumbbells: Cheaper than a yearly gym subscription. With a couple of YouTube videos, they’re enough to keep moving my body every day.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Money and personal development books: I bought print copies of these titles:
    &lt;ul&gt;
      &lt;li&gt;The Monk Who Sold His Ferrari&lt;/li&gt;
      &lt;li&gt;The Ruthless Elimination of Hurry&lt;/li&gt;
      &lt;li&gt;The Richest Man in Babylon&lt;/li&gt;
      &lt;li&gt;The Psychology of Money&lt;/li&gt;
      &lt;li&gt;Think and Grow Rich&lt;/li&gt;
      &lt;li&gt;Unwinding Anxiety&lt;/li&gt;
      &lt;li&gt;Purify Your Brain&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Workstation setup: Since I spend most of my working time in front of a computer, I bought an air conditioner, a standing desk, and an external display.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Writing course: I’ve been writing since 2018 when I wrote &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;my first blog post&lt;/a&gt;. But most of what I had learned was through trial and error. This year, I paid for a writing course and went all in with my writing.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Eating out with friends: Some friends are worth every cup of coffee or dinner out.&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Being In A Self-Managed Team Made Me Try The Most Expensive Hamburgers I&apos;ve Eaten</title>
   <link href="https://canro91.github.io/2024/12/11/SelfManagedTeam/"/>
   <updated>2024-12-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/11/SelfManagedTeam</id>
   <content type="html">&lt;p&gt;I didn’t pay for the most expensive hamburgers I’ve eaten. An ex-employer did.&lt;/p&gt;

&lt;p&gt;When I say “most expensive,” I mean the same greasy $5 or $10-dollar hamburger from a food truck or a corner in a busy street. But they felt expensive because I ate them while working overnight.&lt;/p&gt;

&lt;h2 id=&quot;i-was-in-a-self-managed-team&quot;&gt;I was in a “self-managed team”&lt;/h2&gt;

&lt;p&gt;In a past job, when my team was lagging behind the self-imposed deadlines of sprints, higher-ups told us:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“This sprint is behind schedule. What are you going to do? You’re a self-managed team, so decide that by yourselves. But if I were you, I’d choose to work overtime to keep up.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“You’re a self-managed team” was their subtle way of telling us to work extra hours, without accepting any responsibility.&lt;/p&gt;

&lt;p&gt;We didn’t have any say in what tasks to do and how. We were only a self-managed team when it came to choosing to work overtime.&lt;/p&gt;

&lt;p&gt;They didn’t pay us for that extra time. They only bought us hamburgers. And, when someone raised their concerns about the quality of those hamburgers, they told us we should be grateful because other companies buy their employees nothing to eat.&lt;/p&gt;

&lt;h2 id=&quot;since-i-dont-want-this-to-be-only-a-rant&quot;&gt;Since I don’t want this to be only a rant…&lt;/h2&gt;

&lt;p&gt;If you’re told you’re part of a self-managed team, read the fine print. Look for signs of real self-managed teams:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2024/12/04/RedFlags/&quot;&gt;Look at job descriptions&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Connect with people already working there.&lt;/li&gt;
  &lt;li&gt;Ask follow-up questions during interviews.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise, they might expect you to work overtime without compensation or just buy you an hamburger.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Six Inspiring Lessons I Learned from Jim Kwik, the Brain Coach—in Six Quotes</title>
   <link href="https://canro91.github.io/2024/12/10/JimKwik/"/>
   <updated>2024-12-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/10/JimKwik</id>
   <content type="html">&lt;p&gt;Jim Kwik is “the world’s #1 brain performance coach.” But he wasn’t always someone we would consider smart.&lt;/p&gt;

&lt;p&gt;In school, he was called “broken” because of his learning issues. He wasn’t as fast as his classmates and couldn’t read. An accident caused him brain injuries that put him behind his class.&lt;/p&gt;

&lt;p&gt;Believe it or not, he now teaches the very same subjects he struggled with: learning, reading, and memory. He’s the author of &lt;a href=&quot;https://start.kwikbrain.com/limitless-expanded&quot;&gt;Limitless&lt;/a&gt; and the host of the &lt;a href=&quot;https://www.jimkwik.com/podcast/&quot;&gt;Kwik Brain podcast&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are 6 lessons I learned from Jim Kwik’s videos and podcast episodes:&lt;/p&gt;

&lt;h2 id=&quot;1-little-by-little-a-little-becomes-a-lot&quot;&gt;1. “Little by little, a little becomes a lot”&lt;/h2&gt;

&lt;p&gt;That shows the compounding power of consistency.&lt;/p&gt;

&lt;p&gt;Want to change your life? Learn a new skill and practice it daily for a year. Read a book, learn a new language, or write online. Start with small, simple steps.&lt;/p&gt;

&lt;h2 id=&quot;2-first-you-build-your-habits-then-your-habits-build-you&quot;&gt;2. “First you build your habits, then your habits build you”&lt;/h2&gt;

&lt;p&gt;Wake up at 5:00 AM, take cold showers, drink water, go to the gym, meditate, journal…and on and on. It doesn’t have to be that complicated.&lt;/p&gt;

&lt;p&gt;You can choose a simple routine: create something in the mornings, consume in the afternoons, and disconnect in the evenings.&lt;/p&gt;

&lt;h2 id=&quot;3-reading-is-downloading-decades-of-information-to-your-brain-in-a-few-hours&quot;&gt;3. “Reading is downloading decades of information to your brain in a few hours”&lt;/h2&gt;

&lt;p&gt;Books condense decades of an author’s experiences into thousands of pages.&lt;/p&gt;

&lt;p&gt;Reading is the closest we can get to plugging ourselves into a computer and downloading new programs to our brains in seconds, like in the Matrix. “I know Kung Fu. Show me.”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Reading is the best exercise for your brain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem is most of us haven’t taken a reading class since school.&lt;/p&gt;

&lt;p&gt;If you haven’t, start by using your finger to guide your eyes as you read. It will increase your reading speed and comprehension.&lt;/p&gt;

&lt;p&gt;If knowledge is power, then reading is your superpower.&lt;/p&gt;

&lt;h2 id=&quot;4-yet-opens-up-new-possibilities&quot;&gt;4. “Yet opens up new possibilities”&lt;/h2&gt;

&lt;p&gt;We are our first haters with our negative self-talk:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“I can’t”&lt;/li&gt;
  &lt;li&gt;“I don’t know”&lt;/li&gt;
  &lt;li&gt;“I don’t have”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reframe those ANTs (Automatic Negative Thoughts) with a simple word: “yet.”&lt;/p&gt;

&lt;p&gt;“Yet” brings new possibilities and room for growth:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;“I can’t &lt;strong&gt;yet&lt;/strong&gt;”&lt;/li&gt;
  &lt;li&gt;“I don’t know &lt;strong&gt;yet&lt;/strong&gt;”&lt;/li&gt;
  &lt;li&gt;“I don’t have &lt;strong&gt;yet&lt;/strong&gt;”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple word can change it all.&lt;/p&gt;

&lt;h2 id=&quot;5-you-dont-get-burned-out-by-doing-too-much-but-by-doing-too-little-of-the-things-you-care-about&quot;&gt;5. “You don’t get burned out by doing too much, but by doing too little of the things you care about”&lt;/h2&gt;

&lt;p&gt;Been there, done that. I got burned out when I tried to convince myself to do something I didn’t like that much just for money.&lt;/p&gt;

&lt;p&gt;It took me months to get back my physical and mental health. Doing something just for money was a painful decision.&lt;/p&gt;

&lt;h2 id=&quot;6-use-ai-to-extend-your-hi-not-to-replace-it&quot;&gt;6. “Use AI to extend your HI, not to replace it”&lt;/h2&gt;

&lt;p&gt;AI is a powerful tool, but don’t use it to replace your HI (Human Intelligence). Don’t outsource your learning and thinking to AI.&lt;/p&gt;

&lt;p&gt;Use AI as your copilot, not as the pilot.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Jim Kwik’s story is a story of change, possibility, and determination. From the kid with the “broken” brain to a world-renowned brain and learning expert.&lt;/p&gt;

&lt;p&gt;It’s what you have between your ears that separates you from what you want.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>8 Unexpected Lessons I Learned from Watching One of My Favorite TV Shows—Scorpion</title>
   <link href="https://canro91.github.io/2024/12/09/Scorpion/"/>
   <updated>2024-12-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/09/Scorpion</id>
   <content type="html">
&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2024-12-09-Scorpion/Poster.png&quot; alt=&quot;Scorpion TV show poster&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Scorpion&apos;s team. Via: themoviedb.org&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Imagine The Big Bang Theory marrying MacGyver. That’s Scorpion.&lt;/p&gt;

&lt;p&gt;Scorpion, aired between 2014 and 2018, follows a team of four geniuses (and two or more “normal” people depending on the episode) solving impossible cases for the Department of Homeland Security. Walter O’Brien, the guy with one of the highest IQ in history, 197, leads the team—allegedly, based on a real character.&lt;/p&gt;

&lt;p&gt;In every episode, they solve all sorts of crazy and impossible cases to save the day, the U.S., or the world:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Saving a small county in the middle of nowhere from a fire approaching a nuclear waste facility.&lt;/li&gt;
  &lt;li&gt;Dismantling an old nuclear warhead in a secret military base.&lt;/li&gt;
  &lt;li&gt;Saving a kid trapped in a cave from drowning by raising tides.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And those are only the cases I remember off the top of my head.&lt;/p&gt;

&lt;p&gt;After binge-watching all four seasons of Scorpion, these are 8 lessons I learned:&lt;/p&gt;

&lt;h2 id=&quot;1-soft-skills-take-you-further&quot;&gt;1. Soft skills take you further&lt;/h2&gt;

&lt;p&gt;A team of four geniuses can get around any situation. But the team is in trouble when Paige isn’t around.&lt;/p&gt;

&lt;p&gt;Interestingly, Paige isn’t a genius by conventional standards. She isn’t a mathematician or mechanic. She has excellent social skills and is brilliantly at connecting with people.&lt;/p&gt;

&lt;p&gt;In one episode, while Paige wasn’t around, Toby, the doctor, and Happy, the mechanic, made things worse.&lt;/p&gt;

&lt;p&gt;They needed to collaborate with the local police to save Walter but their poor social skills and egos got them arrested. They ended up insulting the police chief instead of coordinating efforts. They had to call Paige to fix the misunderstanding.&lt;/p&gt;

&lt;p&gt;Hard skills open doors, but soft skills take you further.&lt;/p&gt;

&lt;h2 id=&quot;2-find-something-that-gives-you-meaning&quot;&gt;2. Find something that gives you meaning&lt;/h2&gt;

&lt;p&gt;Before joining the team, everyone was a mess:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Toby, the doctor, was a gambling addict.&lt;/li&gt;
  &lt;li&gt;Paige was an underpaid waitress with no purpose.&lt;/li&gt;
  &lt;li&gt;Paige’s son, Ralph, was misunderstood as a weirdo. He was another genius, living in his own world.&lt;/li&gt;
  &lt;li&gt;Happy, the mechanic, had no friends or family.&lt;/li&gt;
  &lt;li&gt;Sylvester, the Math genius, was bullied or something. I don’t remember exactly.&lt;/li&gt;
  &lt;li&gt;Walter, the leader, had 0 social skills.&lt;/li&gt;
  &lt;li&gt;Agent Gallo, the government handler, was a widower and workaholic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solving those near-impossible cases gave them meaning. Being part of something and working together was what they needed to bring meaning to their lives.&lt;/p&gt;

&lt;h2 id=&quot;3-a-simple-framework-tells-many-stories&quot;&gt;3. A simple framework tells many stories&lt;/h2&gt;

&lt;p&gt;Almost all Scorpion episodes are the same.&lt;/p&gt;

&lt;p&gt;The team is hanging out in the garage. Then, Agent Gallo or an external client comes with a case. They quickly come up with a plan. But, mid-case something unexpected happens that complicates the case. They have no clue how to solve it, but a non-genius (Paige, Gallo, or somebody else) says something unrelated. An “aha” moment leads to a new solution. And, finally, they’re back at the garage to end the episode.&lt;/p&gt;

&lt;p&gt;A simple storytelling framework gets you hooked on every episode from start to end.&lt;/p&gt;

&lt;h2 id=&quot;4-find-whos-best-at-every-job&quot;&gt;4. Find who’s best at every job&lt;/h2&gt;

&lt;p&gt;No matter who is leading a case, the team shines when everyone is working on what they’re good at:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sylvester, mental math.&lt;/li&gt;
  &lt;li&gt;Toby, medicine and psychiatry.&lt;/li&gt;
  &lt;li&gt;Happy, mechanics.&lt;/li&gt;
  &lt;li&gt;Paige, connecting with people.&lt;/li&gt;
  &lt;li&gt;Walter, leading and connecting the dots.&lt;/li&gt;
  &lt;li&gt;Gallo, speaking the government lingo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a leader, your job is to find out who’s best at each task, split the goal into smaller tasks, and assign each task to the right person.&lt;/p&gt;

&lt;h2 id=&quot;5-but-be-willing-to-take-the-back-seat&quot;&gt;5. But, be willing to take the back seat&lt;/h2&gt;

&lt;p&gt;From time to time, someone has to stay at the garage and oversee the entire case: monitoring and reporting, passing context, and coordinating efforts with local authorities.&lt;/p&gt;

&lt;p&gt;As a leader, you’re not there to shine, but to make others shine and get the work done.&lt;/p&gt;

&lt;h2 id=&quot;6-be-ready-to-have-uncomfortable-situations&quot;&gt;6. Be ready to have uncomfortable situations&lt;/h2&gt;

&lt;p&gt;As the series progresses, the team has to face their own challenges.&lt;/p&gt;

&lt;p&gt;Gallo has childhood trauma. Walter has unresolved issues with his father. Happy can’t express her feelings. Sylvester is afraid of pretty much everything.&lt;/p&gt;

&lt;p&gt;They all overcame their issues when they opened up and talked to someone they trust.&lt;/p&gt;

&lt;h2 id=&quot;7-learn-to-let-go-of-what-you-cant-control&quot;&gt;7. Learn to let go of what you can’t control&lt;/h2&gt;

&lt;p&gt;Spoiler alert…&lt;/p&gt;

&lt;p&gt;Happy and Toby missed their wedding ceremony while solving a case. They lost the flight back home because the case took longer. They couldn’t control that and couldn’t do anything about it.&lt;/p&gt;

&lt;p&gt;But they controlled what they did after that. Again, Paige saved the day by improvising a wedding ceremony for them. She’s the real genius in the team.&lt;/p&gt;

&lt;p&gt;Life keeps changing its rules. You can’t control it. Learn to adapt to the new rules and keep playing.&lt;/p&gt;

&lt;h2 id=&quot;8-your-favorite-show-could-end-anytime&quot;&gt;8. Your favorite show could end anytime&lt;/h2&gt;

&lt;p&gt;The show was canceled and the last episode ended with a cliffhanger. Damn! Where’s season 5? Arrggg!&lt;/p&gt;

&lt;p&gt;I guess the lesson here is to always end our stories or writings with a cliffhanger.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I didn’t believe a TV show could teach that much about leadership, storytelling, and psychology…Well, I don’t feel that guilty about watching four seasons with 93 episodes. At least, I took some lessons away and wrote about it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Who Would You Trust: The CEO Or The Janitor?</title>
   <link href="https://canro91.github.io/2024/12/08/CEOVsJanitor/"/>
   <updated>2024-12-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/08/CEOVsJanitor</id>
   <content type="html">&lt;p&gt;More than 25% of new Google code is generated by AI.&lt;/p&gt;

&lt;p&gt;That’s what Sundar Pichai, CEO of Google, announced in the last earnings call—Q3 2024.&lt;/p&gt;

&lt;p&gt;That’s scary. Not because AI is taking our jobs, but because they’re relying on who knows what kind of code. And if human-generated code has created all sorts of problems, what about the AI-generated code?&lt;/p&gt;

&lt;p&gt;That announcement generated lots of discussions.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=42002212&quot;&gt;On Hacker News&lt;/a&gt;, a Google employee, probably behind a throwaway account, said that AI is autocompleting the lines he writes, but it doesn’t do any actual engineering or generate code from natural language. I was wrong about my initial thought.&lt;/p&gt;

&lt;p&gt;On LinkedIn, there was a post dismantling that announcement and pointing to that Hacker News comment. Of course, someone didn’t believe what the Google employee wrote in that comment. “Would you trust a random employee?”&lt;/p&gt;

&lt;p&gt;If you really want to know how a company is run, do you ask the CEO or someone with boots on the ground?&lt;/p&gt;

&lt;p&gt;The best one to answer how a company runs is the receptionist, janitor, or an entry-level employee, not the CEO.&lt;/p&gt;

&lt;p&gt;The CEO of a million-dollar company, to raise stock prices and invite investors to throw money at them, will say “AI,” “disruption,” “climate change,” “diversity,” or any trending SEO keyword.&lt;/p&gt;

&lt;p&gt;I don’t believe what Google’s CEO said. I believe that random Hacker News comment.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>One Simple Trick to Avoid Writer&apos;s Block</title>
   <link href="https://canro91.github.io/2024/12/07/WritersBlock/"/>
   <updated>2024-12-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/07/WritersBlock</id>
   <content type="html">&lt;p&gt;I almost broke my daily writing habit today.&lt;/p&gt;

&lt;p&gt;Starting on November 1st, I began writing daily here on my blog. I chose to go with a good headline and one main idea. That’s good enough to mark the calendar and call it a day. No need for an introduction, 10 main points, and a conclusion.&lt;/p&gt;

&lt;p&gt;I start my writing sessions after sitting in silence for 10 minutes with my eyes closed. Like magic, before finishing those 10 minutes, something comes to mind.&lt;/p&gt;

&lt;p&gt;That was not the case today.&lt;/p&gt;

&lt;p&gt;I looked at Hacker News, searching for something to react to but found nothing interesting. Reddit? Nothing. Old posts to expand on? Nothing. List of drafts? Extra nothing.&lt;/p&gt;

&lt;p&gt;Until I had to step outside for a while.&lt;/p&gt;

&lt;p&gt;There’s something magical and mysterious about taking a walk… Well, there’s nothing magical about that. More oxygen gets to the brain, and that activates certain brain regions.&lt;/p&gt;

&lt;p&gt;On my way back home, I had something to write. I rushed to my laptop before I forgot it.&lt;/p&gt;

&lt;p&gt;If you think you’re facing writer’s block, read something to prime your brain and step away from your computer for a walk. Before you get back, you’ll have something to write. It works every time.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Always Be Writing About What You Do at Work</title>
   <link href="https://canro91.github.io/2024/12/06/AlwaysWriting/"/>
   <updated>2024-12-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/06/AlwaysWriting</id>
   <content type="html">&lt;p&gt;That’s better than simply claiming on your CV that you did something.&lt;/p&gt;

&lt;p&gt;Write about what you’re doing and what you’re learning at work. For example, write about:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Challenges you’re facing while solving a problem.&lt;/li&gt;
  &lt;li&gt;Lessons you’re learning from every project.&lt;/li&gt;
  &lt;li&gt;Checklist you use to review pull requests.&lt;/li&gt;
  &lt;li&gt;Most common code review comments you give.&lt;/li&gt;
  &lt;li&gt;Difficult situations you have overcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;my first post&lt;/a&gt; to document an alternative for a coding task I had. For my &lt;a href=&quot;/AdventOfCode2022&quot;&gt;2022 Advent of Posts&lt;/a&gt;, I took a lot of inspiration from what I was doing at work at that time.&lt;/p&gt;

&lt;p&gt;There are always alternatives to avoid disclosing real code. 99% of the time, we’re not doing top-secret rocket science. But companies don’t want the world to know how they’re doing CRUD applications.&lt;/p&gt;

&lt;p&gt;You can share isolated coding blocks and use different business domains to represent examples and coding issues. Using movies, posts, and reservations is a good alternative.&lt;/p&gt;

&lt;p&gt;Instead of saying “trust me, I know how to do that,” you could say “I’ve done it and here’s where I wrote about it.”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How a Layoff Feels—And How to Prepare for the Next One</title>
   <link href="https://canro91.github.io/2024/12/05/HowALayoffFeels/"/>
   <updated>2024-12-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/05/HowALayoffFeels</id>
   <content type="html">&lt;p&gt;Do you have a minute?&lt;/p&gt;

&lt;p&gt;That was the last message you got from your boss’ boss.&lt;/p&gt;

&lt;p&gt;Earlier that day, you logged in to work as usual. And you had a couple of “Are you still around?” messages. You knew it was happening. Again.&lt;/p&gt;

&lt;p&gt;Then came a quick goodbye message from another colleague. He shared his email and contact details.&lt;/p&gt;

&lt;p&gt;You went through the daily meeting knowing something was in the air. Some team members didn’t show up. Everybody pretended nothing was happening. But everyone knew it. It was the most awkward and useless daily meeting of all.&lt;/p&gt;

&lt;p&gt;This “Do you have a minute?” conversation was different. You felt the disturbance in the Force right from the start. Your boss’ boss looked downwards, his voice is trembling. A small speech of how bad the economy was going. Your only thought was, “It’s my turn?”&lt;/p&gt;

&lt;p&gt;Then the bomb: “We have to let you go this time.”&lt;/p&gt;

&lt;p&gt;Definitely, it was your turn.&lt;/p&gt;

&lt;p&gt;You left that last meeting relieved and worried at the same time. “What am I going to do now?”&lt;/p&gt;

&lt;p&gt;And just like that, you got disconnected from the VPN and the company chat.&lt;/p&gt;

&lt;p&gt;It was nice while it lasted.&lt;/p&gt;

&lt;p&gt;That’s how being laid off feels. I know. I’ve been there. More than once.&lt;/p&gt;

&lt;p&gt;Layoffs are always around the corner. High interest rates. A recession. AI…Today it’s a large U.S. company. Tomorrow who knows?&lt;/p&gt;

&lt;p&gt;We’re better off rolling our own insurance policy:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Building a brand&lt;/li&gt;
  &lt;li&gt;Starting a side hustle&lt;/li&gt;
  &lt;li&gt;Creating an emergency fund&lt;/li&gt;
  &lt;li&gt;Growing a professional network&lt;/li&gt;
  &lt;li&gt;Having multiple sources of income&lt;/li&gt;
  &lt;li&gt;Learning different skills to monetize&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Winter is always coming. Make yourself layoff-proof.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>If You&apos;re Looking for Red Flags Once You&apos;re in a Job, It&apos;s Too Late</title>
   <link href="https://canro91.github.io/2024/12/04/RedFlags/"/>
   <updated>2024-12-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/04/RedFlags</id>
   <content type="html">&lt;p&gt;It all starts with the job description.&lt;/p&gt;

&lt;p&gt;No job description? Red flag.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“We’re looking for a passionate coding ninja to join our family. We work in an agile and fast-paced environment. We’re looking for a coder with 5 years of experience who can work on our public web page, mobile app, backend, frontend, DevOps, security, compliance, sales, marketing, documentation…Compensation based on experience and interview results.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’m making that up, but I wouldn’t be surprised if there’s a “real” job description like that one out there.&lt;/p&gt;

&lt;p&gt;Passionate, coding ninja/superhero/master, family, fast-paced, high pressure…Nothing screams danger more than those words in a job description. Run, Forrest, run!&lt;/p&gt;

&lt;p&gt;After reading between the lines of the job description, look at the company website.&lt;/p&gt;

&lt;p&gt;“We’re a family.” That was the opening line on the careers page of a company someone tried to refer me to. I stopped reading. I didn’t need to look at anything else. It was a hell no. That was a blinking danger sign with sirens.&lt;/p&gt;

&lt;p&gt;Ironically, every company claims to offer excellent conditions, and every applicants seems to be the perfect candidate for the job.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Who Isn&apos;t This For?</title>
   <link href="https://canro91.github.io/2024/12/03/WhoThisIsNotFor/"/>
   <updated>2024-12-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/03/WhoThisIsNotFor</id>
   <content type="html">&lt;p&gt;It’s more important to ask who this isn’t for than who this is for.&lt;/p&gt;

&lt;p&gt;Who isn’t this course for? Who isn’t this coaching program for? Who isn’t this blog post for?&lt;/p&gt;

&lt;p&gt;I created a Udemy course on unit testing, one of my favorite subjects. I answered who this course is for on the landing page.&lt;/p&gt;

&lt;p&gt;One student said some parts were too advanced. He had just started with the subject and my course covers more advanced material. It wasn’t for him.&lt;/p&gt;

&lt;p&gt;So I asked for feedback. What were those challenging parts? There’s room for improvement there. Extra lessons and better lesson descriptions.&lt;/p&gt;

&lt;p&gt;And I quickly updated the landing page to show who this course isn’t for. That will attract fewer students but the right ones. The ones my course is really for.&lt;/p&gt;

&lt;p&gt;Whatever you’re doing, answer who this isn’t for.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stop Being Afraid of Hitting Publish on LinkedIn With These 9 Proven Strategies</title>
   <link href="https://canro91.github.io/2024/12/02/FearOfPublishing/"/>
   <updated>2024-12-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/02/FearOfPublishing</id>
   <content type="html">&lt;p&gt;Publishing your first LinkedIn posts is scary.&lt;/p&gt;

&lt;p&gt;I know that feeling. Your hands shake. Your heart is beating. You keep retyping your password. You’re publishing something on the Internet for the first time ever.&lt;/p&gt;

&lt;p&gt;You’re in fight or flight mode, ready to run away from lions behind a bush.&lt;/p&gt;

&lt;p&gt;What if I say something wrong? What if my boss reads what I write? What if that comes up in future interviews? What if someone makes fun of me? It’s normal to have these worries.&lt;/p&gt;

&lt;p&gt;I felt the same way when I wrote for the first time on LinkedIn, even after writing on my blog for years.&lt;/p&gt;

&lt;p&gt;If the fear of being exposed online prevents you from hitting “Post” or “Schedule” on LinkedIn (or anywhere else online), follow these strategies:&lt;/p&gt;

&lt;h2 id=&quot;1-publish-a-lot&quot;&gt;1. Publish a lot&lt;/h2&gt;

&lt;p&gt;The first time you do anything is scary.&lt;/p&gt;

&lt;p&gt;The only way to overcome it is with repetition. Write a lot and publish a lot. Create a writing schedule you can sustain and then challenge yourself to double it.&lt;/p&gt;

&lt;p&gt;Back in January 2024, I decided to revive my LinkedIn account by writing 100 short-form posts. I started with one post a week, then two, then three, and settled with one post per day, every day. Fear of writing? Gone!&lt;/p&gt;

&lt;p&gt;If you write every day, you will hit 100 posts in about three months. The fear of writing and being exposed will disappear.&lt;/p&gt;

&lt;h2 id=&quot;2-schedule-your-posts&quot;&gt;2. Schedule your posts&lt;/h2&gt;

&lt;p&gt;Otherwise, after hours of crafting a perfect post, you will find yourself with the cursor on the “Post” button hesitant to click it. And you will delay it for tomorrow. And often, tomorrow means never.&lt;/p&gt;

&lt;p&gt;Schedule your posts, forget about them, and come back the next day.&lt;/p&gt;

&lt;h2 id=&quot;3-turn-off-email-notifications&quot;&gt;3. Turn off email notifications&lt;/h2&gt;

&lt;p&gt;LinkedIn (and any other social media company) will try to keep you on their platform.&lt;/p&gt;

&lt;p&gt;They will trick you with all kinds of emails. Someone posted. Someone commented. “Someone on LinkedIn viewed your profile.” Really, LinkedIn?&lt;/p&gt;

&lt;p&gt;Turn off all email notifications and continue with your day. Otherwise, you’ll be refreshing and refreshing your browser tab and checking your email, wondering why nobody has liked or commented on your first masterpieces.&lt;/p&gt;

&lt;h2 id=&quot;4-understand-sturgeons-law&quot;&gt;4. Understand Sturgeon’s law&lt;/h2&gt;

&lt;p&gt;90% of everything is crap. That’s Sturgeon’s law.&lt;/p&gt;

&lt;p&gt;That’s not to discourage you. It’s to lower your expectations when writing for the first time. 90% of everything is crap. Your first posts will be in that 90%. Your first posts will be far from perfect.&lt;/p&gt;

&lt;p&gt;Keep writing and keep improving.&lt;/p&gt;

&lt;p&gt;I wrote my first blog post back in 2018. And “post” is a strong word. It was a word vomit. I dumped a bunch of words into a document and published it online. I still have that first post unedited to remind me how I started.&lt;/p&gt;

&lt;p&gt;Same story with my first LinkedIn post. I made all LinkedIn sins possible in a single post. Only added an external link. Zero formatting for mobile devices. Lots of hashtags.&lt;/p&gt;

&lt;p&gt;If after writing for months, you don’t cringe when reading your first posts, you haven’t improved much.&lt;/p&gt;

&lt;h2 id=&quot;5-remember-the-303030-rule&quot;&gt;5. Remember the 30/30/30 rule&lt;/h2&gt;

&lt;p&gt;No matter what you do:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;30% of people will love it.&lt;/li&gt;
  &lt;li&gt;Another 30% will hate it.&lt;/li&gt;
  &lt;li&gt;And another 30% won’t even care.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I learned that reading “Choose Yourself” by &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Write for your 30% and forget about the other 60%.&lt;/p&gt;

&lt;h2 id=&quot;6-think-of-writing-like-buying-lottery-tickets&quot;&gt;6. Think of writing like buying lottery tickets&lt;/h2&gt;

&lt;p&gt;Would you expect to win the lottery the first time you buy a ticket? Nope!&lt;/p&gt;

&lt;p&gt;Writing is the same. Every time you hit “Post,” you’re buying a lottery ticket. If you don’t win today, you buy another ticket tomorrow. If your post didn’t get the likes you wanted, there’s a chance it will next day.&lt;/p&gt;

&lt;p&gt;Keep publishing.&lt;/p&gt;

&lt;h2 id=&quot;7-make-it-about-business-career-or-work&quot;&gt;7. Make it about business, career, or work&lt;/h2&gt;

&lt;p&gt;Every social network has its own vibe.&lt;/p&gt;

&lt;p&gt;LinkedIn isn’t the exception. If LinkedIn were a person, he would be a 30-year-old office worker wearing a suit and minding his language because his boss or future boss is watching.&lt;/p&gt;

&lt;p&gt;Everything you write on LinkedIn make it about work, business, and careers.&lt;/p&gt;

&lt;p&gt;If you’re not sure what to write about, share:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Lessons from your last job&lt;/li&gt;
  &lt;li&gt;Lessons from your best boss&lt;/li&gt;
  &lt;li&gt;The best book you have read&lt;/li&gt;
  &lt;li&gt;The best course you have taken&lt;/li&gt;
  &lt;li&gt;Lessons you wish you knew before starting your job&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Share anything you wish you knew two years ago about your work or career.&lt;/p&gt;

&lt;p&gt;You don’t have to share personal details or post selfies. Don’t publish what you wouldn’t publish in a local newspaper or niche magazine.&lt;/p&gt;

&lt;h2 id=&quot;8-be-a-journalist&quot;&gt;8. Be a journalist&lt;/h2&gt;

&lt;p&gt;What should I write about if I’m not an expert? Don’t let that thought stop you.&lt;/p&gt;

&lt;p&gt;Don’t try to be an expert. Who’s an expert anyway? Share what you have learned and what you’re learning. Show your work.&lt;/p&gt;

&lt;p&gt;Don’t create, document. Imagine you’re a journalist with a part-time job doing whatever you’re doing now.&lt;/p&gt;

&lt;h2 id=&quot;9-find-a-writing-buddy&quot;&gt;9. Find a writing buddy&lt;/h2&gt;

&lt;p&gt;Writing your first posts can feel lonely. Probably, only your coworkers will like them when they log in to LinkedIn once every full moon or when they’re looking for a new job.&lt;/p&gt;

&lt;p&gt;Find someone in the same journey as you and work together.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;Your first posts are the most challenging. Going from zero to one is the hardest part. Don’t be afraid of hitting “Post” or “Schedule.” The more you hit it, the less scary it becomes.&lt;/p&gt;

&lt;p&gt;No matter what you write about or where you write, an online presence is the new CV and portfolio. And LinkedIn is a good place to start. Safe and professional. The boss is watching, remember?&lt;/p&gt;

&lt;p&gt;Take a deep breath and write your first post. Commit to your next 20, 30, or even 100 posts and see what opportunities they bring.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Who Gets Promoted Higher and Faster at Work?</title>
   <link href="https://canro91.github.io/2024/12/01/WhoGetsPromoted/"/>
   <updated>2024-12-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/12/01/WhoGetsPromoted</id>
   <content type="html">&lt;p&gt;It’s not the best coder. It’s not the one who cranks out more pull requests or lines of code.&lt;/p&gt;

&lt;p&gt;The one who gets promoted higher and faster is the one with the best soft skills. The best communicator. The one who’s best at &lt;a href=&quot;/2024/11/15/TalkingToNonTechies/&quot;&gt;explaining complex subjects to non-tech people&lt;/a&gt;. The best at dealing with people.&lt;/p&gt;

&lt;p&gt;The best coders are left alone to continue cranking out code in a corner. “Ok, kid. When will you have your PR ready?”&lt;/p&gt;

&lt;p&gt;And when the best coders, with poor soft skills, are put in charge of teams, projects tend to go sideways. They fail to communicate expectations, provide context, and coordinate people. They continue thinking in terms of lines of code and pull requests, not in team dynamics.&lt;/p&gt;

&lt;p&gt;Start improving your soft skills by reading “How to Make Friends and Influence People.”&lt;/p&gt;

&lt;p&gt;If you could take away just one lesson from that book: never tell anyone he’s wrong.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This Is Why We Don&apos;t Test Private Methods</title>
   <link href="https://canro91.github.io/2024/11/30/TestingPrivateMethods/"/>
   <updated>2024-11-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/30/TestingPrivateMethods</id>
   <content type="html">&lt;p&gt;Trying to test private methods causes a lot of confusion.&lt;/p&gt;

&lt;p&gt;That’s a common question we all have made when finding unit testing for the first time. These days, I found that very same question on &lt;a href=&quot;https://www.reddit.com/r/csharp/comments/1gf2r6s/trying_to_understand_why_we_dont_test_private/&quot;&gt;Reddit&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Can someone explain to me why unit testing our private methods is bad?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because we don’t want to break encapsulation.&lt;/p&gt;

&lt;p&gt;If you have a private method, how are you going to call it from a test class or method? It’s private. You can only access it from inside the same class. That’s the whole point of access modifiers: restricting access to the fields and properties that hold the internal state of a class.&lt;/p&gt;

&lt;p&gt;And please don’t make your private methods public and static to call them directly inside unit tests. They’re private for a reason, right? We don’t want the rest of your code to use them directly.&lt;/p&gt;

&lt;p&gt;Exposing internals is &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;the most common mistake when writing tests&lt;/a&gt;. I’ve &lt;a href=&quot;/2022/12/22/TestingDuplicatedEmails/&quot;&gt;seen it and fixed it before&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s take the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasAdmin()&lt;/code&gt; method from the question as an example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasPermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unless &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasAdmin()&lt;/code&gt; has 0 references—if that’s the case, you should remove it—another method from the same class is calling it. And you can trace the chain of method calls back to a public method.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasAdmin()&lt;/code&gt;, or any other private method down in the chain of method calls, is changing something that you can observe from public methods. Probably it’s affecting a return value or changing an internal state you can inspect with getters. That’s what you should test instead.&lt;/p&gt;

&lt;p&gt;To test &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasAdmin()&lt;/code&gt;, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; object with the right relations and permissions, call the public methods you have, and check what should change when your user is an admin or not. Maybe you return additional data only admins can access or finish an action without throwing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnauthorizedAccessException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You test private methods indirectly while testing the observable behavior exposed through public methods.&lt;/p&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;

&lt;p&gt;To read more about unit testing, check &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;how to write your first unit test in C# with MSTest&lt;/a&gt; and &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;four common mistakes when writing your first unit tests&lt;/a&gt;. Don’t miss the rest of the &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Always Be Reading. But Reading More Isn&apos;t Always the Answer</title>
   <link href="https://canro91.github.io/2024/11/29/ReadingMore/"/>
   <updated>2024-11-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/29/ReadingMore</id>
   <content type="html">&lt;p&gt;Before finding “The Almanack of Naval Ravikant,” I was a ferocious reader.&lt;/p&gt;

&lt;p&gt;I was in the “let’s read as many books as we can” team. By pure FOMO, I was trying to follow YouTube trends like “I read 9,999 books about money, here’s what I learned” and the mantra “read one book per week.”&lt;/p&gt;

&lt;p&gt;But out of dozens of books I had read, I didn’t remember reading some of them, even when I had notes. I kept one or two ideas from those books in the back of my head but I couldn’t trace them back to where I found them.&lt;/p&gt;

&lt;p&gt;Then I found Naval’s reading strategy. Ironically, by reading another book.&lt;/p&gt;

&lt;p&gt;Instead of reading as many books as possible, he reads and rereads a few good ones. The ones that have passed the test of time. He meditates on their lessons, acts on them, and then he uses X/Twitter to take public notes.&lt;/p&gt;

&lt;p&gt;Naval’s reading strategy changed my mind about reading:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;I don’t have to read books from cover to cover.&lt;/strong&gt; I can jump straight to a chapter or section to find an answer.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;I can leave books unfinished.&lt;/strong&gt; For someone trying to increase a book count, leaving books unfinished made it hard to count. If I read only half of the book, should I add 0.5 to my tally?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;It’s OK to reread books.&lt;/strong&gt; Again, rereading didn’t contribute to my increasing book count.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And more important than reading to increase a book count is acting on what we read. Remember, &lt;a href=&quot;/2024/11/02/PassiveLearning/&quot;&gt;passive learning is just entertainment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you read a book, write 10 lessons you learned from that book, and find one lesson you can act on immediately. That’s more valuable than a large book count without any taking any action.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Find Your Producer&apos;s Switch</title>
   <link href="https://canro91.github.io/2024/11/28/ProducerSwitch/"/>
   <updated>2024-11-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/28/ProducerSwitch</id>
   <content type="html">&lt;p&gt;That’s Leland Sklar’s strategy, a well-known bass guitar player and session musician. He’s appeared on over 2,000 albums, based on his Wikipedia profile.&lt;/p&gt;

&lt;p&gt;While recording in a studio, if a director asked him to change the sound, he simply turned on or off a fake switch and continued playing.&lt;/p&gt;

&lt;p&gt;This is what he said during an interview for &lt;a href=&quot;https://www.guitarworld.com/features/the-truth-behind-lee-sklars-custom-producers-switch&quot;&gt;Guitar World&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“If I’m on a session and the producer asks me to get a different sound, I make sure he sees me flip this switch and then I just change my hand position a bit. There are no wires or anything that go to this switch. It’s a placebo, but it’s saved me a lot of grief in the studio.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s genius!&lt;/p&gt;

&lt;p&gt;What would be a director’s switch for coders? A typo in a variable name, missing brackets on 1-line conditionals, misaligned elements on a screen, writing SELECT * instead of SELECT with a list of columns.&lt;/p&gt;

&lt;p&gt;What would you add?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Easiest Way to Be Ignored When Communicating at Work</title>
   <link href="https://canro91.github.io/2024/11/27/BeingIgnoredAtWork/"/>
   <updated>2024-11-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/27/BeingIgnoredAtWork</id>
   <content type="html">&lt;p&gt;Only send “Hello, how are you?” in Teams or Slack at work.&lt;/p&gt;

&lt;p&gt;You’ll get ignored immediately. Especially if you’re working in a remote team with people all over the world. And especially if you’re reaching out to a busy manager or executive.&lt;/p&gt;

&lt;h2 id=&quot;this-is-how-that-hello-how-are-you-conversation-will-look&quot;&gt;This is how that “Hello, how are you?” conversation will look:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Hello, how are you?&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
  &lt;li&gt;Good, thanks. And you?&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
  &lt;li&gt;Good too, thanks.&lt;/li&gt;
  &lt;li&gt;Hey, I just wanted…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, what if your recipient is on the other side of the world? 24 hours could pass between each message. And for sure, you don’t want to wait 48 hours to start the real conversation.&lt;/p&gt;

&lt;p&gt;While working remotely at a past job, I used &lt;a href=&quot;https://nohello.net/en/&quot;&gt;nohello.net&lt;/a&gt; as my Slack status. That page shows a fake “hello, how are you?” conversation, getting ignored. And I still got “Hello, how are you?” messages. Arrrggg!&lt;/p&gt;

&lt;h2 id=&quot;would-you-send-an-email-saying-just-hello-how-are-you-at-work&quot;&gt;Would you send an email saying just “Hello, how are you” at work?&lt;/h2&gt;

&lt;p&gt;I know you wouldn’t. Then, why do the same on Slack or Teams at work?&lt;/p&gt;

&lt;p&gt;Next time you want to reach out to someone at work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t send “Hello, how are you?” Say hi and in the same message, without waiting for an answer, say what you really want to say.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That should be “Remote Working 101” when &lt;a href=&quot;/2024/12/28/BetterOnboardings/&quot;&gt;onboarding new team members at any remote company&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Did You Have a Mentor, and Did It Help You?</title>
   <link href="https://canro91.github.io/2024/11/26/FindingMentors/"/>
   <updated>2024-11-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/26/FindingMentors</id>
   <content type="html">&lt;p&gt;That’s question I found on &lt;a href=&quot;https://dev.to/m_midas/did-you-have-a-mentor-and-did-it-help-you-55pd&quot;&gt;dev.to&lt;/a&gt;. Here’s my full answer:&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I’ve had mentors. But, not formally and without the label.&lt;/p&gt;

&lt;p&gt;I can pinpoint two or three people in my career that were like “mentors.”&lt;/p&gt;

&lt;p&gt;At a past job, I had the chance to work next to my team architects and learned a lot from them. From the value of reading other people’s code to rejecting requests politely. &lt;a href=&quot;/2022/12/18/LessonsFromExCoworkers/&quot;&gt;Here are some of those lessons&lt;/a&gt;, by the way.&lt;/p&gt;

&lt;p&gt;And at another job, one day I decided to reach out to my boss’ boss with genuine questions, not related to the job. He was open and kind enough to answer them. He liked reading books too and we connected on that. From that point, we started to have 1-1s outside the usual meetings. He recommended a long list of books to me.&lt;/p&gt;

&lt;p&gt;If you’re looking for a mentor, don’t ask anyone to “be your mentor.” That would imply commitment from one side. Most of the time, for free.&lt;/p&gt;

&lt;p&gt;Instead of asking someone “to be your mentor”:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Approach him with genuine questions&lt;/strong&gt;: Bring your own challenges and how you’re trying to face them. Ask what he would do if he were in your shoes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Show interest in him&lt;/strong&gt;: Ask your “not-mentor” about his career progression and choices. People like talking about themselves.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Help him somehow too&lt;/strong&gt;: When I found books my not-mentor would like, I also brought them to our conversations.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Show you’re taking action on the advice or input you’re given&lt;/strong&gt;: I followed up with my not-mentor, sharing lessons I learned from the books he recommended to me.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With those four steps, you will be in a mentorship—without even realizing it.&lt;/p&gt;

&lt;p&gt;But, don’t worry if you don’t have a mentor or can’t find one.&lt;/p&gt;

&lt;p&gt;Mentors are everywhere. The good thing is you don’t have to meet them. 200-500 books are a good mentor. I learned that from &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;, a mentor I haven’t met.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I&apos;d Learn a Language for Work From Scratch</title>
   <link href="https://canro91.github.io/2024/11/25/LearningALanguageForWork/"/>
   <updated>2024-11-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/25/LearningALanguageForWork</id>
   <content type="html">&lt;p&gt;It took me two years to learn English.&lt;/p&gt;

&lt;p&gt;And by “learning English” I mean, a traditional language school gave me a signed piece of paper that says I speak English. After two years, lots of repeat-after-me sessions and grammar exercises, someone finally said I was able to speak English.&lt;/p&gt;

&lt;p&gt;I didn’t start learning English from scratch. We’re taking English classes in school since kindergarten, probably.&lt;/p&gt;

&lt;p&gt;But if I had to start from almost scratch to learn a language for work, here’s what I’d do:&lt;/p&gt;

&lt;h2 id=&quot;with-ai&quot;&gt;With AI&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Make a list of common situations at work: Daily meetings, 1-1s, or retrospectives.&lt;/li&gt;
  &lt;li&gt;Ask ChatGPT or Copilot to generate short conversations or stories for those situations using the most common words. For example, a daily meeting where you report that you’re stuck with a task.&lt;/li&gt;
  &lt;li&gt;Use &lt;a href=&quot;https://elevenlabs.io/&quot;&gt;elevenlabs&lt;/a&gt; or any other text-to-speech tool to turn those conversations into spoken words.&lt;/li&gt;
  &lt;li&gt;Rinse and repeat.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;with-youtube&quot;&gt;With YouTube&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Take a beginner’s course on a subject you already know. In your target language, of course. The goal is not to learn that particular subject but to learn new vocabulary.&lt;/li&gt;
  &lt;li&gt;Turn the course subtitles or script into phrases to assemble and interchange.&lt;/li&gt;
  &lt;li&gt;Rinse and repeat.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Et voilà!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>In a Couple of Decades, There Won&apos;t Be Many People Who Can Write</title>
   <link href="https://canro91.github.io/2024/11/24/WriteNots/"/>
   <updated>2024-11-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/24/WriteNots</id>
   <content type="html">&lt;p&gt;There will only be writes and write-nots.&lt;/p&gt;

&lt;p&gt;That’s Paul Graham’s prediction about the &lt;a href=&quot;https://paulgraham.com/writes.html&quot;&gt;future of writing in the days of AI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are my comments on Paul Graham’s post:&lt;/p&gt;

&lt;h2 id=&quot;1-to-write-well-you-have-to-think-clearly-and-thinking-clearly-is-hard&quot;&gt;1. “To write well you have to think clearly, and thinking clearly is hard”&lt;/h2&gt;

&lt;p&gt;Clear thinking is &lt;a href=&quot;/2024/11/08/WhyWriting/&quot;&gt;the most important reason to write&lt;/a&gt;. You need to order your thoughts before putting them on paper or in a document.&lt;/p&gt;

&lt;p&gt;If you know how to write and how to speak in public, you’re unstoppable. I heard that from Jordan Peterson somewhere on YouTube.&lt;/p&gt;

&lt;p&gt;Write to think and don’t outsource your thinking.&lt;/p&gt;

&lt;h2 id=&quot;2-writing-pervades-many-jobs&quot;&gt;2. “Writing pervades many jobs”&lt;/h2&gt;

&lt;p&gt;That’s true for us, coders and software engineers too. Writing is everywhere in our software projects. From README files to user stories to product announcements.&lt;/p&gt;

&lt;p&gt;And, the higher up you go, it’s less about coding and more about communication. And except for meetings, you’ll spend more time writing than coding.&lt;/p&gt;

&lt;h2 id=&quot;3-almost-all-pressure-to-write-has-dissipated&quot;&gt;3. “Almost all pressure to write has dissipated”&lt;/h2&gt;

&lt;p&gt;Sure, AI can generate blog posts, sales pages, and any other form of writing.&lt;/p&gt;

&lt;p&gt;But, by design, AI generates average writing. That’s its goal. We can tell it by looking at the opening line of any AI-generated text. “In the realm of…” or “In the world of…” Nothing screams AI more than that.&lt;/p&gt;

&lt;p&gt;AI will bring more average content. We’re already seeing it. Try searching anything in Google these days.&lt;/p&gt;

&lt;p&gt;The printing press didn’t kill books. Social networks didn’t kill bars. Instragram didn’t kill photography.&lt;/p&gt;

&lt;p&gt;AI won’t kill writing. AI will only make writing more valuable than ever.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Big Problems Demand Small Solutions</title>
   <link href="https://canro91.github.io/2024/11/23/BigProblems/"/>
   <updated>2024-11-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/23/BigProblems</id>
   <content type="html">&lt;p&gt;That was what Seth Godin answered during &lt;a href=&quot;https://www.youtube.com/watch?v=vPiH5uGSVyo&quot;&gt;an interview with Jim Kwik&lt;/a&gt; for the Kwik Brain podcast. Jim asked him about mindset shifts for people stuck in the ideation phase, struggling to take action.&lt;/p&gt;

&lt;p&gt;Then Seth expanded his answer by saying:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Let’s start with a small solution first…Let’s figure out what’s the smallest habit change that would lead you to create the conditions for the system to support you in where you’re going.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Seth used back pain as an example. Before going through back surgery, we should change shoes, lose weight, and walk. Those steps require 10 minutes a day. Less effort and commitment than surgery.&lt;/p&gt;

&lt;p&gt;I connect that idea with &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher’s Daily Practice&lt;/a&gt;. By doing something every day for our body, mind, and spirit, we’re creating the conditions to support change in our lives.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Everybody Has An Accent. And That&apos;s Okay</title>
   <link href="https://canro91.github.io/2024/11/22/EverybodyHasAnAccent/"/>
   <updated>2024-11-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/22/EverybodyHasAnAccent</id>
   <content type="html">&lt;p&gt;In professional settings, native speakers aren’t the ones who reject non-native speakers by their speaking skills. Other non-native speakers are.&lt;/p&gt;

&lt;p&gt;The other day I referred a friend (with good English skills. B2, probably) to the same software agency I was working with. He got rejected. The recruiter (another non-native speaker) rejected him because he had “a strong accent like someone from India.”&lt;/p&gt;

&lt;p&gt;Arrrggg! You know what…&lt;/p&gt;

&lt;p&gt;Everybody has an accent.&lt;/p&gt;

&lt;p&gt;I have an accent, even when speaking my native language. There’s no such thing as a neutral accent, maybe only on TV and in films.&lt;/p&gt;

&lt;p&gt;The purpose of learning a second language is communication, not perfection.&lt;/p&gt;

&lt;p&gt;Fluency is about connecting with others, not about making 0 mistakes. And, at the end of the day, nobody speaks perfectly in any language.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Simple Way to Start Your Creative Journey? Put Your Fingerprint on Something Every Day</title>
   <link href="https://canro91.github.io/2024/11/21/PutYourFingerprint/"/>
   <updated>2024-11-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/21/PutYourFingerprint</id>
   <content type="html">&lt;p&gt;There are 7 billion people and 7 billion unique fingerprints.&lt;/p&gt;

&lt;p&gt;This means that anything you put your fingerprint on is unique.&lt;/p&gt;

&lt;p&gt;To start your creative journey, recreate something you like, by adding or subtracting your own taste. You will put your fingerprint on it. You will make it unique.&lt;/p&gt;

&lt;p&gt;I’m putting my fingerprint on this idea. I found it in Reinvent Yourself by &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;. He found it somewhere else. He put his fingerprint on it and now I’m putting mine.&lt;/p&gt;

&lt;p&gt;That relieves the pressure of coming up with something original. Just put your fingerprint on something.&lt;/p&gt;

&lt;p&gt;You find a quote? Put your fingerprint on it by rephrasing it and adding your own taste.&lt;/p&gt;

&lt;p&gt;You find an interesting book passage? Put your fingerprint on it by sharing it along with a story.&lt;/p&gt;

&lt;p&gt;Sooner or later, people will find your “fingerprinted” versions and follow your work by your own fingerprints.&lt;/p&gt;

&lt;p&gt;Take the challenge of putting your fingerprint on something every day. I’m putting my fingerprint on a corner of the Internet with every post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Read 500 Books About a Subject to Reinvent Yourself</title>
   <link href="https://canro91.github.io/2024/11/20/Read500Books/"/>
   <updated>2024-11-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/20/Read500Books</id>
   <content type="html">&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;What is “it”? How do I know what I should do?&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;Whatever area you feel like reading 500 books about. Go to the bookstore and find it. If you get bored three months later go back to the bookstore.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s from “Reinvent Yourself” by &lt;a href=&quot;/2024/11/11/LessonsFromJamesAltucher/&quot;&gt;James Altucher&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When he was bankrupt, to get back up again, he started to read 500 books about money. He recommends doing the same.&lt;/p&gt;

&lt;p&gt;But I haven’t read 500 books in my life, let alone in one particular subject. Not even about software engineering and programming where I’ve put close to 10,000 hours of practice. I’ve read maybe a couple dozen books about programming and coding.&lt;/p&gt;

&lt;p&gt;Let’s say we read 1 book per week. That’s 52 books per year. And that’s ~10 years of reading to reach 500 books. Or less if we choose not to read some books from cover to cover.&lt;/p&gt;

&lt;p&gt;An easier alternative is to read 3 books about a subject to stay ahead of the 90%. That’s from The 4-Hour Work Week. That’s way easier. And definitely, anyone can read more than 3 books about a subject. That’s the rule I’ve been following so far to learn new subjects.&lt;/p&gt;

&lt;p&gt;Is it better to go all in on a subject with 500 books or mix subjects in between? Don’t know.&lt;/p&gt;

&lt;p&gt;The first subject to read 500 books about should be meta-learning, learning how to learn. That would be the groundwork for any future endeavor or experiment.&lt;/p&gt;

&lt;p&gt;If after some books we realize we’re not interested in that subject anymore, we’re free to jump to another subject and start from “0 books read” again.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why Learn a (Foreign) Language — Even When We Have AI and Many Other Tools These Days</title>
   <link href="https://canro91.github.io/2024/11/19/WhyLearnForeignLanguages/"/>
   <updated>2024-11-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/19/WhyLearnForeignLanguages</id>
   <content type="html">&lt;p&gt;Apart from monetary reasons—learning a second language helped me double my last salary as a full-time employee:&lt;/p&gt;

&lt;h2 id=&quot;1-because-ai-cant-replace-human-connection&quot;&gt;1. Because AI can’t replace human connection&lt;/h2&gt;

&lt;p&gt;From “How to Win Friends and Influence People,” the trick to having good conversations is to make them about the other person.&lt;/p&gt;

&lt;p&gt;And the easiest way to make conversations about the other person is to use a few words in their native language. Hi, bye, yes/no, thanks, and please will create a connection immediately.&lt;/p&gt;

&lt;p&gt;In a past job, I learned a few words in Russian to connect with my coworkers. It was funny when they forgot to use English in shared chats and a simple “English, please” in Russian (anglijski, pozhaluysta) broke the ice.&lt;/p&gt;

&lt;p&gt;AI can’t replace smiles when you greet people in their native language.&lt;/p&gt;

&lt;h2 id=&quot;2-because-ai-cant-replace-the-excitement-of-decoding-what-was-once-gibberish&quot;&gt;2. Because AI can’t replace the excitement of decoding what was once gibberish&lt;/h2&gt;

&lt;p&gt;My German is limited to survival phrases.&lt;/p&gt;

&lt;p&gt;But the last time I visited my best friend’s parents in Europe, while taking a walk around a park, someone asked my best friend’s mom in German: “Ist das dein Sohn?” (Is that your son?) My inner child couldn’t avoid jumping in excitement. I got that!&lt;/p&gt;

&lt;p&gt;That’s one of the most exciting moments of learning a language. Passing from hearing noise to decoding and understanding sounds.&lt;/p&gt;

&lt;p&gt;An “I got that!” is priceless.&lt;/p&gt;

&lt;h2 id=&quot;3-because-ai-cant-reshape-our-brains-and-makes-us-think-different&quot;&gt;3. Because AI can’t reshape our brains and makes us think different.&lt;/h2&gt;

&lt;p&gt;Languages shape our thinking.&lt;/p&gt;

&lt;p&gt;When we learn a new language, we’re creating new connections in our brain. Think of learning a language with a new script or with sounds that don’t exist in your native language. Every language rewires our brain.&lt;/p&gt;

&lt;p&gt;These new connections make us think and behave differently.&lt;/p&gt;

&lt;p&gt;We’re a new person with every language we learn. AI can’t replace that. Yet?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Coders Often Don&apos;t Get To Solve Problems</title>
   <link href="https://canro91.github.io/2024/11/18/CodersDontSolveProblems/"/>
   <updated>2024-11-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/18/CodersDontSolveProblems</id>
   <content type="html">&lt;p&gt;Coding is about solving problems with automation.&lt;/p&gt;

&lt;p&gt;The most interesting and funniest part is figuring out a coding solution to a problem.&lt;/p&gt;

&lt;p&gt;But often, by the time a coder is involved, all the big picture thinking and decision making have already been made, killing all the fun.&lt;/p&gt;

&lt;p&gt;Somebody else already talked to customers. Somebody else decided what to do and how long it will take. Somebody else divided the work into milestones. No software engineers or people with boots on the ground were involved.&lt;/p&gt;

&lt;p&gt;At a past job, our VP, probably to look smart in front of other executives, promised to finish in one month a project that needed at least 6 months. He picked a number out of thin air without asking anyone.&lt;/p&gt;

&lt;p&gt;Coders are only involved to turn JIRA tickets into lines of code.&lt;/p&gt;

&lt;p&gt;The most rewarding and funniest projects have been when I have all the context around customer needs and am involved in most of the design and architecture decisions.&lt;/p&gt;

&lt;p&gt;If someone else talks to customers and writes specs and we, as coders, only turn those specs into code, we’ll be out of business soon. &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;AI will replace us all&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Best Time To Look for a New Job</title>
   <link href="https://canro91.github.io/2024/11/17/BestTimeToLookForANewJob/"/>
   <updated>2024-11-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/17/BestTimeToLookForANewJob</id>
   <content type="html">&lt;p&gt;It’s not when you’re “let go.”&lt;/p&gt;

&lt;p&gt;The best time to look for a job is when you don’t need one.&lt;/p&gt;

&lt;p&gt;You’re not desperate to pick anything to just pay the bills. You’re in a better position to negotiate. You can use your new offer as leverage in your current job. You don’t have anything to loose.&lt;/p&gt;

&lt;p&gt;That was a lesson I shared with a group of friends and ex-coworkers the last time we met to catch up.&lt;/p&gt;

&lt;p&gt;After the usual questions, where you’re working these days and how you’re feeling in your new role or job, one of our ex-coworkers answered he felt good in his new role after a couple of years and he wasn’t looking for something new. Then I shared that lesson. I wish I knew it earlier.&lt;/p&gt;

&lt;p&gt;It doesn’t matter if you want to leave your current job or not. Don’t outsource your career decisions to your boss or something else. At least, set a direction for your career and be conscious about the jobs you pick.&lt;/p&gt;

&lt;p&gt;For some years, I make the mistake of outsourcing my career choices.&lt;/p&gt;

&lt;p&gt;A couple of years ago, I knew it was time to leave my job, but instead of making a hard decision, I asked for a raise. If they refused to give me a raise, that was the sign to leave. And, surprised, surprise, I got the pay raise. Arrggg!&lt;/p&gt;

&lt;p&gt;Vacations and pay raises won’t change an unfulfilling job. They will only move a death sentence further away, like kicking a can.&lt;/p&gt;

&lt;p&gt;That taught me that the best time to look for a new job is not right after a layoff, it’s when you don’t need a new job.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Quotes I&apos;m Trying to Live By</title>
   <link href="https://canro91.github.io/2024/11/16/QuotesToLiveBy/"/>
   <updated>2024-11-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/16/QuotesToLiveBy</id>
   <content type="html">&lt;h2 id=&quot;1-im-a-simple-man-making-his-way-through-the-galaxy-like-my-father-before-me&quot;&gt;#1. “I’m a simple man making his way through the galaxy, like my father before me”&lt;/h2&gt;

&lt;p&gt;If you’re a Star Wars fan, you recognize that phrase. That’s by Boba Fett from The Mandalorian.&lt;/p&gt;

&lt;p&gt;We’re all here trying to figure out our paths. Some are more intentional about it, others are not. Some have found it, others are still searching.&lt;/p&gt;

&lt;p&gt;Every stage in life will bring its own challenges and paths to figure out.&lt;/p&gt;

&lt;h2 id=&quot;2-the-minute-you-learn-something-teach-it&quot;&gt;#2. “The minute you learn something, teach it”&lt;/h2&gt;

&lt;p&gt;That’s from &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt; by Austen Kleon. “A book for people who hate the very idea of self promotion.”&lt;/p&gt;

&lt;p&gt;That phrase puts into words one of my core values.&lt;/p&gt;

&lt;p&gt;It has kept my curious inner child alive. Once I find something new, my sense of curiosity or inner child, wants to share it with somebody else. Most of the time, by writing about it.&lt;/p&gt;

&lt;p&gt;When you teach what you learn, you have the chance of learning it twice.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Learn To Talk to Non-Tech People in Your Team</title>
   <link href="https://canro91.github.io/2024/11/15/TalkingToNonTechies/"/>
   <updated>2024-11-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/15/TalkingToNonTechies</id>
   <content type="html">&lt;p&gt;Want to stand out as a coder? Learn to talk to non-technical people.&lt;/p&gt;

&lt;p&gt;That’s a crucial skill to master for every coder, even if you’re not on the path to be a team leader.&lt;/p&gt;

&lt;h2 id=&quot;jargon-wont-make-you-look-smarter&quot;&gt;Jargon won’t make you look smarter&lt;/h2&gt;

&lt;p&gt;We often take pride in using technical jargon. It makes us feel smart.&lt;/p&gt;

&lt;p&gt;But true mastery is explaining things to people outside our fields.&lt;/p&gt;

&lt;p&gt;At a previous team, we found a bug when syncing restaurant bills to room charges. It took us a long time to fix it. It was a serious issue.&lt;/p&gt;

&lt;p&gt;The next day, we had “that” conversation in our daily meeting.&lt;/p&gt;

&lt;p&gt;The team lead started to explain: &lt;em&gt;“API, background processor, eventual consistency, optimistic concurrency…“&lt;/em&gt; He used every bit of jargon he knew to justify our 1- or 2-day delay.&lt;/p&gt;

&lt;p&gt;To our PM and other non-tech people, it sounded like the alien language from &lt;em&gt;The Arrival&lt;/em&gt;. You could tell by their confused and “what are you talking about” faces.&lt;/p&gt;

&lt;h2 id=&quot;speak-their-language-not-yours&quot;&gt;Speak their language, not yours&lt;/h2&gt;

&lt;p&gt;In that daily meeting, a simpler &lt;em&gt;“somebody could walk away without paying their restaurant bills. And nobody here wants to pay for that”&lt;/em&gt; would have finished the conversation with some laughs and a point made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always make it about the person listening.&lt;/strong&gt; Often our job as coders is being interpreters: from business language to technical language and vice-versa.&lt;/p&gt;

&lt;p&gt;Try explaining your current project to a friend who doesn’t code, using their own words. If they get it, you’re winning.&lt;/p&gt;

&lt;p&gt;Explaining complex ideas simply is one of the most underrated skills to stand out as a coder. Truth is, the senior you get, the less it’s about coding and the more about communication. That’s why I made clear communication one of the strategies in my book, &lt;em&gt;Street-Smart Coding: 30 Ways to Get Better at Coding.&lt;/em&gt; That’s the practical guide I wish I had on my journey from junior to senior.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://imcsarag.gumroad.com/l/streetsmartcoding/?utm_source=blog&amp;amp;utm_medium=post&amp;amp;utm_campaign=talk-to-non-tech-people&quot;&gt;Get your copy of Street-Smart Coding here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Getting Rid of Nulls Is Indeed A Good Idea</title>
   <link href="https://canro91.github.io/2024/11/14/NoNulIsAGoodIdea/"/>
   <updated>2024-11-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/14/NoNulIsAGoodIdea</id>
   <content type="html">&lt;p&gt;These days I found a Medium article titled: &lt;a href=&quot;https://levelup.gitconnected.com/why-eliminating-nulls-from-your-code-will-not-make-your-app-better-f64082f9a162&quot;&gt;Why Eliminating NULLs From Your Code Will Not Make Your App Better&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Its point is that when we stop using null, we replace checking for null with checking for a default value or a wrapper like Result. And there’s no major gain.&lt;/p&gt;

&lt;h2 id=&quot;but-there-is-a-gain-when-eliminating-null&quot;&gt;But there is a gain when eliminating null&lt;/h2&gt;

&lt;p&gt;The advantage of returning a wrapper like &lt;a href=&quot;/2023/03/20/UseOptionInsteadOfNull/&quot;&gt;Option or Result instead of null&lt;/a&gt; is making the implicit explicit.&lt;/p&gt;

&lt;p&gt;Instead of getting a &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;NullReferenceException&lt;/a&gt; and saying “ooops, that method returned null,” we look at a method signature and say “Hey, that method might return null. We’re better off checking for that.”&lt;/p&gt;

&lt;p&gt;That’s &lt;strong&gt;Method Signature Honesty&lt;/strong&gt;. When the parameters and return types of a method show what it does, even for edge cases.&lt;/p&gt;

&lt;p&gt;For example, what does this method do?&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMovieById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What if there was no movie found? What if there was more than one? Does the method throw an exception? Returns null? Returns a Movie.Empty? We can’t tell just by looking at the signature.&lt;/p&gt;

&lt;p&gt;But what about?&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMovieById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMovieById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At least, it’s obvious from that signature that there’s a special case we need to handle. It’s up to the caller to decide what to do with it.&lt;/p&gt;

&lt;p&gt;The original article has a point that replacing a null checking like,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMovieById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Do something here&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMovieById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* Do something here */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// And at some point later:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* Nothing found. Do something else here */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;is a design choice. For C#, the first one looks like more “native.”&lt;/p&gt;

&lt;p&gt;In either case, at some point, we have to convert the absence of data (either a null or a None) to something else like a status code or error message.&lt;/p&gt;

&lt;h2 id=&quot;c-doesnt-have-an-explicit-type-to-avoid-null&quot;&gt;C# doesn’t have an explicit type to avoid null&lt;/h2&gt;

&lt;p&gt;C# didn’t take the wrapper route and introduced &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;nullable references&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;With nullable references turned on, all our object references are not null by default. If we want a reference to accept null, we should annotate the type with a ?. The same way we do it for nullable ints.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMovieById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  ^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// It might be null.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yes, that name is kind of misleading. It should be “not nullable” references.&lt;/p&gt;

&lt;p&gt;That’s a feature we should turn on and make all nullable warnings as errors.&lt;/p&gt;

&lt;p&gt;With Option, Result, or nullable references, we make our method signatures honest. That’s already a gain.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Join my course &lt;a href=&quot;https://www.udemy.com/course/csharp-nullreferenceexception-demystified/?referralCode=CC2A6F51EF27A75F1364&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;NRECourse-Footer&quot; data-goatcounter-title=&quot;NRE Course: Footer&quot;&gt;C# NullReferenceException Demystified&lt;/a&gt; on Udemy and learn the principles, features, and strategies to avoid this exception in just 1 hour and 5 minutes.
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Blogging Tips to Write More</title>
   <link href="https://canro91.github.io/2024/11/13/BlogMore/"/>
   <updated>2024-11-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/13/BlogMore</id>
   <content type="html">&lt;p&gt;Inspired by &lt;a href=&quot;https://seths.blog/&quot;&gt;Seth Godin&lt;/a&gt; and &lt;a href=&quot;https://herbertlui.net/&quot;&gt;Herbert Lui&lt;/a&gt;, I looked up other people writing daily on their personal blogs and found these two pieces of advice:&lt;/p&gt;

&lt;h2 id=&quot;1-if-its-longer-than-a-tweet-280-characters-it-can-be-a-post&quot;&gt;1. If it’s longer than a Tweet, 280 characters, it can be a post.&lt;/h2&gt;

&lt;p&gt;That’s from Mike Crittenden’s &lt;a href=&quot;https://critter.blog/2020/09/28/2-months-of-daily-blogging/&quot;&gt;2 months of daily blogging&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Post length doesn’t matter if we transmit the message clearly in a few words. A post can simply be a headline and a main idea. Don’t feel guilty about short posts.&lt;/p&gt;

&lt;h2 id=&quot;2-instead-of-leaving-a-comment-write-a-reaction-post-and-share-a-link-instead&quot;&gt;2. Instead of leaving a comment, write a reaction post and share a link instead.&lt;/h2&gt;

&lt;p&gt;Credits to &lt;a href=&quot;https://untested.sonnet.io/notes/instead-or-writing-a-comment-write-a-post-and-link-it/&quot;&gt;Rafał Pastuszak&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A reaction post, even a short one, is calmer and thoughtful. Often, we write most comment in “the heat” of reading or without even reading the entire piece.&lt;/p&gt;

&lt;p&gt;That sounds like one tip to &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;never run of writing ideas&lt;/a&gt; and a way to &lt;a href=&quot;/2024/11/09/LimitedKeystrokes/&quot;&gt;preserve keystrokes&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two Alternatives to Assertion Messages — and Two Ideas if You Do Want To Keep Them</title>
   <link href="https://canro91.github.io/2024/11/12/AssertionMessagesAlternatives/"/>
   <updated>2024-11-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/12/AssertionMessagesAlternatives</id>
   <content type="html">&lt;p&gt;I got this question from Ankush on my contact page:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“I want to override the Assert method to avoid using Assert.True without a failure message. How can I achieve it?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here are 2 alternatives to assertion messages and 2 ideas if you do want them:&lt;/p&gt;

&lt;h2 id=&quot;1-dont-use-assertion-messages-write-better-test-names-instead&quot;&gt;1. Don’t use assertion messages. Write better test names instead.&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;Test naming conventions&lt;/a&gt;, like the UnitOfWork_Scenario_Result, impose some order and structure to names. You won’t have that inside assertion messages.&lt;/p&gt;

&lt;p&gt;If you don’t have too many assertions in your tests, with a good test name it’s easy to figure out what failed and why.&lt;/p&gt;

&lt;h2 id=&quot;2-use-assertion-libraries-with-better-messages&quot;&gt;2. Use assertion libraries with better messages.&lt;/h2&gt;

&lt;p&gt;Like &lt;a href=&quot;https://github.com/fluentassertions/fluentassertions&quot;&gt;FluentAssertions&lt;/a&gt; or &lt;a href=&quot;https://github.com/shouldly/shouldly&quot;&gt;Shoudly&lt;/a&gt;. They have clearer and more verbose messages when an assertion fails.&lt;/p&gt;

&lt;p&gt;Here’s what MSTest, FluentAssertions, and Shoudly show when an assertion fails:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UnitTest1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ADummyTestToShowFailureMessages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anyBool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// With MSTest&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anyBool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Message:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Assert.IsTrue failed.&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// With FluentAssertions&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;anyBool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  Message:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Expected anyBool to be True, but found False.&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// With Shouldly&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;anyBool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldBe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//   Message: &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Test method AssertionLibraries.UnitTest1.ADummyTestToShowFailureMessages threw exception: &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Shouldly.ShouldAssertException: anyBool&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    should be&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// True&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    but was&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// False&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s if you want assertion messages for the lack of good failure messages.&lt;/p&gt;

&lt;h2 id=&quot;now-if-you-do-want-assertion-messages-for-whatever-reason&quot;&gt;Now, if you do want assertion messages for whatever reason:&lt;/h2&gt;

&lt;h2 id=&quot;3-write-custom-assertion-methods&quot;&gt;3. Write custom assertion methods&lt;/h2&gt;

&lt;p&gt;Wrap MSTest, NUnit, XUnit or the testing library you’re using inside custom methods with the assertion messages as required parameters. Like,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;AcmeCorp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CustomAssert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yourMessageHereAsARequiredParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re doing code reviews, that’s something to share with your team and enforce during reviews.&lt;/p&gt;

&lt;h2 id=&quot;4-create-a-custom-analyzer-to-check-the-absence-of-assertion-messages&quot;&gt;4. Create a custom analyzer to check the absence of assertion messages&lt;/h2&gt;

&lt;p&gt;Code reviews can be daunting. And even if all test methods in your test suite have assertion messages, anyone can decide not to use them.&lt;/p&gt;

&lt;p&gt;Automate the boring part with an analyzer that warns you if you’re not passing a message to every assertion.&lt;/p&gt;

&lt;p&gt;Here’s an analyzer from &lt;a href=&quot;https://github.com/xunit/xunit.analyzers/blob/main/src/xunit.analyzers/X2000/AssertEqualShouldNotBeUsedForNullCheck.cs&quot;&gt;xunit.analyzer&lt;/a&gt;. That’s a good place to start.&lt;/p&gt;

&lt;p&gt;Voilà!&lt;/p&gt;

&lt;p&gt;To read more about assertions, check &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;how to write better assertions&lt;/a&gt; and &lt;a href=&quot;/2021/08/16/WriteCustomAssertions/&quot;&gt;how to write custom Assertions to improve your tests&lt;/a&gt;. Don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>9 Lessons I Learned After Following James Altucher&apos;s Work</title>
   <link href="https://canro91.github.io/2024/11/11/LessonsFromJamesAltucher/"/>
   <updated>2024-11-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/11/LessonsFromJamesAltucher</id>
   <content type="html">&lt;p&gt;It’s hard to define what James Altucher does.&lt;/p&gt;

&lt;p&gt;He’s an investor, podcaster, writer, chess player, and comedian. That’s only what he shares on podcast interviews. Who knows what his next career will be.&lt;/p&gt;

&lt;p&gt;I found James Altucher’s work by pure accident. I was looking for ways to get better at writing and found the advice of writing 10 headlines a day. That was inspired by James’ ideas.&lt;/p&gt;

&lt;p&gt;Since then, I’ve been binge-watching and binge-reading James Altucher’s work in &lt;a href=&quot;https://www.quora.com/profile/James-Altucher/answers&quot;&gt;Quora&lt;/a&gt;, &lt;a href=&quot;https://jaltucher.medium.com/&quot;&gt;Medium&lt;/a&gt;, and &lt;a href=&quot;https://archive.jamesaltucher.com/&quot;&gt;his own website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A guy that has made millions and lost it all, not just once but at least twice has a lot to teach. Here is what I’ve learned from him.&lt;/p&gt;

&lt;h2 id=&quot;1-if-you-dont-know-what-to-do-with-your-life-start-with-your-health&quot;&gt;1. If you don’t know what to do with your life, start with your health&lt;/h2&gt;

&lt;p&gt;That’s what I’ve been doing after losing my last job. An end and a start of something new that I still don’t know what it will be.&lt;/p&gt;

&lt;p&gt;Uncertainty in a single word. I’ve been questioning my life choices since then.&lt;/p&gt;

&lt;p&gt;When facing those moments of uncertainty, James teaches to start with your health and get 1% better every day. That means doing something for your body, mind, and spirit. That’s what he calls the “Daily Practice.”&lt;/p&gt;

&lt;p&gt;Since then, my daily practice has been:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Starting the day with a glass of water,&lt;/li&gt;
  &lt;li&gt;Working out,&lt;/li&gt;
  &lt;li&gt;Cutting sugar and processed foods,&lt;/li&gt;
  &lt;li&gt;Having a moment of silence, and&lt;/li&gt;
  &lt;li&gt;Writing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-write-10-ideas-a-day&quot;&gt;2. Write 10 ideas a day&lt;/h2&gt;

&lt;p&gt;This is part of the 1% daily improvement: Do something creative. Every. Day.&lt;/p&gt;

&lt;p&gt;Turn yourself into an idea machine by &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;writing 10 ideas a day&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After losing it all for the first time, James bought cheap waiter’s pads and started to write down ideas to get up again. He then wrote ideas for others and gave them away for free. Those ideas brought new opportunities for him.&lt;/p&gt;

&lt;p&gt;Write 10 ideas a day or your idea muscle will atrophy.&lt;/p&gt;

&lt;h2 id=&quot;3-always-be-learning-from-everyone-then-write-about-it&quot;&gt;3. Always be learning from everyone, then write about it&lt;/h2&gt;

&lt;p&gt;Everyone and every situation around us have something to teach us. Notice it and write about it. That could be part of your 10-ideas-a-day practice.&lt;/p&gt;

&lt;p&gt;That’s what I’m doing right now by writing what I’ve learned from James.&lt;/p&gt;

&lt;h2 id=&quot;4-you-dont-need-to-meet-your-mentors&quot;&gt;4. You don’t need to meet your mentors&lt;/h2&gt;

&lt;p&gt;You need mentors in your life. But you don’t have to meet them.&lt;/p&gt;

&lt;p&gt;I don’t know James in the real world. I will probably never meet him. But he has inspired me a lot since I’ve been following his work. He’s a virtual mentor.&lt;/p&gt;

&lt;p&gt;Imagine that your mentor has already passed away and to learn from him, you have to follow the clues he left in his work. Find someone who inspires you and read his books and writings.&lt;/p&gt;

&lt;p&gt;The best mentors are books.&lt;/p&gt;

&lt;h2 id=&quot;5-find-your-plus-equal-and-minus&quot;&gt;5. Find your Plus, Equal, and Minus&lt;/h2&gt;

&lt;p&gt;To get better at any skill, you don’t only need mentors. You need to find your Plus, Equal, and Minus.&lt;/p&gt;

&lt;p&gt;Plus is the people that inspire you. Your teachers, mentors, and favorite authors. James is one of those. Tim Denning, Dan Koe, and Nicolas Cole are some of my Plus in the writing space.&lt;/p&gt;

&lt;p&gt;Equal is the people taking the same path with you. Your peers, coworkers, and friends. These people challenge you to continue improving.&lt;/p&gt;

&lt;p&gt;Minus is the people you can teach. People one step behind the path you’re taking.&lt;/p&gt;

&lt;h2 id=&quot;6-making-keeping-and-growing-money-are-different-skills&quot;&gt;6. Making, keeping, and growing money are different skills&lt;/h2&gt;

&lt;p&gt;James lost it all multiple times. He shares that he was good at making money, but not at keeping it and growing it. He made the wrong investments and bad purchases.&lt;/p&gt;

&lt;p&gt;Money is a totally different set of skills to master.&lt;/p&gt;

&lt;h2 id=&quot;7-reinvent-yourself-every-5-years&quot;&gt;7. Reinvent yourself every 5 years&lt;/h2&gt;

&lt;p&gt;That’s from “Reinvent Yourself,” one of James’ books. And that’s from 2017 when AI wasn’t mainstream.&lt;/p&gt;

&lt;p&gt;These days the need for reinvention is urgent. Our world is changing dramatically fast. Relying on the same skills we learned five years ago is too risky.&lt;/p&gt;

&lt;p&gt;Reinvention means finding new sources of income.&lt;/p&gt;

&lt;p&gt;To start your reinvention, start reading 500 books on a subject. If you don’t know where to start, find a book you like, read it, and read another 499 about the same subject. You’re free to drop it and start all over again.&lt;/p&gt;

&lt;h2 id=&quot;8-have-multiple-sources-of-joy&quot;&gt;8. Have multiple sources of joy&lt;/h2&gt;

&lt;p&gt;That’s from an episode of the podcast &lt;a href=&quot;https://www.youtube.com/watch?v=I6i9KDTw8xQ&quot;&gt;School of Greatness&lt;/a&gt; on YouTube.&lt;/p&gt;

&lt;p&gt;During that episode, James shared he had businesses, writing, comedy, and chess. When things didn’t go as expected in one area, he went to another one.&lt;/p&gt;

&lt;p&gt;I wish I had learned that before.&lt;/p&gt;

&lt;p&gt;For some time, I only tried to find joy in my day job. That was a painful mistake. When things went sideways, I got burned out. I had stopped exercising and doing any of my hobbies. Eventually, all of that made me fall sick. Literally.&lt;/p&gt;

&lt;p&gt;Lesson learned the hard way! Have multiple sources of joy.&lt;/p&gt;

&lt;h2 id=&quot;9-be-like-google-my-friend&quot;&gt;9. Be like Google, my friend&lt;/h2&gt;

&lt;p&gt;Google is the go-to resource for information. (Until AI takes over)&lt;/p&gt;

&lt;p&gt;We don’t spend too much time on Google. We ask questions and it takes us to somewhere else. In spite of that, we keep going back to Google and it’s one of the most valuable companies in the world. That’s the Google effect.&lt;/p&gt;

&lt;p&gt;Be like Google. Be a bank. Be the source of information. Connect people with answers.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;James has a captivating and inspiring story. He has made millions and lost it all, and he’s open about it. From a Computer Science major to be someone difficult to put in a box and label, with multiple careers and skills.&lt;/p&gt;

&lt;p&gt;James’ story teaches you can choose yourself, reinvent yourself, and skip the lines.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>It&apos;s Better To Be Poor Doing Something You Love, Than To Be Rich Doing Something You Hate</title>
   <link href="https://canro91.github.io/2024/11/10/PoorDoingSomethingYouLove/"/>
   <updated>2024-11-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/10/PoorDoingSomethingYouLove</id>
   <content type="html">&lt;p&gt;That’s a quote I found last week while watching a conversation between Tom Bilyeu and Rich Roll &lt;a href=&quot;https://www.youtube.com/watch?v=prlqWU54NME&quot;&gt;on YouTube&lt;/a&gt;. They were discussing Tom’s mindset changes and work history.&lt;/p&gt;

&lt;p&gt;That quote is an invitation to reflect what we’re willing to give up in exchange for money.&lt;/p&gt;

&lt;p&gt;It reflects what I’ve been going through since last year.&lt;/p&gt;

&lt;p&gt;In 2023, I hit rock-bottom. But it wasn’t a money thing. In fact, I was making the most money I had made in my career as a software engineer.&lt;/p&gt;

&lt;p&gt;Things were good until they weren’t.&lt;/p&gt;

&lt;p&gt;I woke up and didn’t feel the drive to show up to work. I wasn’t learning and having fun. I was just for the money.&lt;/p&gt;

&lt;p&gt;Every Sunday evening I felt anxious anticipating next Monday routine. Another daily meeting, another JIRA ticket, another Pull Request…Arrggg!&lt;/p&gt;

&lt;p&gt;I knew I needed to do something but I was too comfortable and afraid of taking risks. I tried to control things around me. And I failed at that.&lt;/p&gt;

&lt;p&gt;All my frustration lead to anger and resentment. It made sick literally.&lt;/p&gt;

&lt;p&gt;That’s why when I was “let go” at the begging of 2024, I felt relieved.&lt;/p&gt;

&lt;p&gt;I was scared of making a change by myself. And life, God, or the Universe gave me a little “push.”&lt;/p&gt;

&lt;p&gt;I was fortunate to have enough “f*ck-you” money to finance my living expenses all these months.&lt;/p&gt;

&lt;p&gt;Now, I’m reinventing my career.&lt;/p&gt;

&lt;p&gt;I’m taking care of my health. I read and write. I’ve been recording some coding courses and doing some freelancing on the side.&lt;/p&gt;

&lt;p&gt;I haven’t figured out everything yet. I need a way to make money more consistently. I need to refill my “f*ck-you” money pockets. I don’t know when I will need them again.&lt;/p&gt;

&lt;p&gt;Definitely, Mondays are not the same when you wake up to do something you love.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Your Keystrokes Are Limited, Here&apos;s How You Preserve Them</title>
   <link href="https://canro91.github.io/2024/11/09/LimitedKeystrokes/"/>
   <updated>2024-11-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/09/LimitedKeystrokes</id>
   <content type="html">&lt;p&gt;Preserve your keystrokes. They’re limited.&lt;/p&gt;

&lt;p&gt;That’s a piece of advice I learned from one of &lt;a href=&quot;/2021/12/06/ItsNotWhatYouRead/&quot;&gt;Scott Hanslman’s talks about productivity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This isn’t an invitation to write less or write shorter content. It’s an invitation to make private knowledge public.&lt;/p&gt;

&lt;p&gt;Often valuable knowledge and lessons die in email threads or Slack channels. Apart from the original recipients, nobody else can read them. That’s how you “waste” your keystrokes.&lt;/p&gt;

&lt;p&gt;Make those private email threads or Slack messages public by turning them into wiki entries, tutorials, or blog posts. That’s how you preserve your keystrokes.&lt;/p&gt;

&lt;p&gt;I’m answering almost all my questions in public to preserve my keystrokes.&lt;/p&gt;

&lt;p&gt;The other day, one of my &lt;a href=&quot;/2024/10/10/EmailList/&quot;&gt;email subscribers&lt;/a&gt; replied to one of my emails with a question about writing. I replied and then turned that reply &lt;a href=&quot;/2024/09/23/StrugglingToWrite/&quot;&gt;into a post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another day, a reader contacted me after going through my takeaways from &lt;a href=&quot;/2020/07/14/UltralearningTakeaways/&quot;&gt;Ultralearning&lt;/a&gt;. And again, I expanded my reply &lt;a href=&quot;/2023/07/24/AdviceToStartAnUltralearningProject/&quot;&gt;into a separate post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don’t let knowledge die in private channels. Preserve your keystrokes and share a link to a public writing instead.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This Is the Real Reason Why Coders (and Everyone Else) Should Write</title>
   <link href="https://canro91.github.io/2024/11/08/WhyWriting/"/>
   <updated>2024-11-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/08/WhyWriting</id>
   <content type="html">&lt;p&gt;Don’t write to attract recruiters or create a brand.&lt;/p&gt;

&lt;p&gt;Sure. Those are benefits of writing online. When we write online, good and unexpected things happen.&lt;/p&gt;

&lt;p&gt;By writing online, we gain followers, build a brand, attract opportunities, and skip hiring lines.&lt;/p&gt;

&lt;p&gt;By luck or accident, I made my first internet money writing, because I showed some of my posts.&lt;/p&gt;

&lt;p&gt;But the real reason to write online is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clear thinking.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Write to think clearly. Write to formulate your ideas coherently, with no fluff.&lt;/p&gt;

&lt;p&gt;Before you write, you have to put your thoughts in order. You need a message. You need structure. You need a reader persona.&lt;/p&gt;

&lt;p&gt;That’s more valuable than recruiters knocking at your door with “life-changing opportunities for well-known companies in the tech sector.”&lt;/p&gt;

&lt;p&gt;Writing is the most valuable and transferable skill you can learn.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>From Now On Here&apos;s How I&apos;m Answering: What Stocks Should I Buy?</title>
   <link href="https://canro91.github.io/2024/11/07/CompaniesToBuy/"/>
   <updated>2024-11-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/07/CompaniesToBuy</id>
   <content type="html">&lt;p&gt;“What companies should I buy in my brokerage account?”&lt;/p&gt;

&lt;p&gt;That was a question that popped up in a Whatsapp group with some of my ex-coworkers and colleagues. In fact, it was not the first time that question popped up. From the same person.&lt;/p&gt;

&lt;p&gt;Here’s what I answered:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If you don’t want to be a full-time investor, go with a broad market index fund. If you don’t know which one, pick any that tracks the S&amp;amp;P500, like VOO or SPY.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But I was wrong.&lt;/p&gt;

&lt;p&gt;Not that my answer was wrong. I’m not a money expert. But I’ve read some money books and changed my mind about money. I realized that the question itself shows something more: not doing the homework and outsourcing thinking to somebody else.&lt;/p&gt;

&lt;p&gt;It’s like asking: “Do you know anyone I should marry?”&lt;/p&gt;

&lt;p&gt;You’re completely at the good intentions of the other person. He should guess the traits you’re looking for in a partner and go through all his relatives, friends, and acquaintances to see if there’s anyone with those traits. He misses lots of things to answer that question.&lt;/p&gt;

&lt;p&gt;I’m not answering, “what companies I should buy in &lt;insert broker=&quot;&quot; name=&quot;&quot;&gt;?&quot; anymore.&lt;/insert&gt;&lt;/p&gt;

&lt;p&gt;The right answer is:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Don’t ask your poor friends for money advice. The most expensive advice is the one you get for free.&lt;/li&gt;
  &lt;li&gt;If you don’t know what to invest in, start investing in yourself, so you can decide on your own where to put your money.&lt;/li&gt;
  &lt;li&gt;Never let somebody else tell you what to do with your own money.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only thing I can share is: “Here is what I do with my money…”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Ask the jeweler for jewelry advice.” — The Richest Man in Babylon&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s how I’m answering that question in the future.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>LinkedIn Shouldn&apos;t Have a Desperate Frame and You Shouldn&apos;t Look That Needy</title>
   <link href="https://canro91.github.io/2024/11/06/LookingNeedy/"/>
   <updated>2024-11-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/06/LookingNeedy</id>
   <content type="html">&lt;p&gt;Hiring in 2024 has been slow.&lt;/p&gt;

&lt;p&gt;I know! I was “let go” in February and tried to find a job for a couple of months until I gave up and decided to jump solo.&lt;/p&gt;

&lt;p&gt;AI, high interest rates, or anything else have changed the job market. There aren’t as many open applications as years before. And the few ones opened get flooded with applications. Pure radio silence after you apply.&lt;/p&gt;

&lt;p&gt;Before, you only needed the right keywords in your LinkedIn headline to get recruiters offering you “life-changing opportunities.” I cringe while typing those words and remembering all those messages.&lt;/p&gt;

&lt;p&gt;Those days seem to be gone.&lt;/p&gt;

&lt;p&gt;The other day the LinkedIn algorithm showed me a post of a young designer showing her profile picture around a ribbon that said “Desperate.” It was her idea to complement the “Hiring” and “Open to work” ribbons or photo frames or whatever LinkedIn called them.&lt;/p&gt;

&lt;p&gt;(If you wrote that post, I totally understand. I did the same thing in a different way a couple of months ago.)&lt;/p&gt;

&lt;p&gt;It’s desperate seeing a bank account going to zero and having bills to pay.&lt;/p&gt;

&lt;p&gt;Writing “I’m desperate and need money. Please give me a job” in a LinkedIn post is the equivalent of wearing dirty clothes, growing a beer, and extend your hand in the middle of a busy street.&lt;/p&gt;

&lt;p&gt;Nobody is going to give you a job for being desperate and looking needy. Other LinkedIn users aren’t going to take money out of their pockets to help you either.&lt;/p&gt;

&lt;p&gt;Instead of giving companies and hiring managers homework and looking desperate and needy, turn around the equation and give away ideas.&lt;/p&gt;

&lt;p&gt;For the young designer with the “Desperate” ribbon idea:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Find 10 companies you’d like to work with&lt;/li&gt;
  &lt;li&gt;Create a brand redesign for each company. Show how you’d change colors, fonts, and logo. Do it like you’re being paid for it.&lt;/li&gt;
  &lt;li&gt;For each redesign, publish a post showing your redesign and tag the company and the head of the creative team (or a hiring manager or the head of the team that hires designers inside a company)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if the companies you’re redesigning don’t reply, others will see your work and skills in action. You don’t have to say “Trust me, I design.” You’re &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;showing your work&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;After being laid off, I sent my CV to as many places as I could find that needed a code monkey. Even &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;I applied to a FAANG&lt;/a&gt; when I rejected that idea early in my career.&lt;/p&gt;

&lt;p&gt;Eventually, I got a reply. And after the first contact and a signed agreement, the head of the company told me: “I don’t know where to put you.” I was waiting for the company to close a new client. In the meantime, I gave them homework.&lt;/p&gt;

&lt;p&gt;I knocked at their doors and said “Please give me work. I do anything.” That was the implicit message I sent when I filled out an application from in their company page and &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;sent my CV&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I should have knocked at their doors with &lt;a href=&quot;/2024/11/05/IdeaMachine/&quot;&gt;10 ideas&lt;/a&gt;, like I recently learned from James Altucher.&lt;/p&gt;

&lt;p&gt;A professional never begs. And, no. LinkedIn shouldn’t have a “Desperate” ribbon.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Want to Be More Creative? Become an Idea Machine—It&apos;ll Change Your Life</title>
   <link href="https://canro91.github.io/2024/11/05/IdeaMachine/"/>
   <updated>2024-11-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/05/IdeaMachine</id>
   <content type="html">&lt;p&gt;Writing 10 ideas every day saved James Altucher from bankruptcy and changed his life.&lt;/p&gt;

&lt;p&gt;If you don’t know James Altucher’s story: he’s an American investor and author. He sold one of his first businesses for millions and lost everything. Not once, but a couple of times.&lt;/p&gt;

&lt;p&gt;In his darkest moments, about to end his life so his family could claim his $4M life insurance, he bought cheap waiter’s pads and started to write ideas to change his life and make money.&lt;/p&gt;

&lt;p&gt;In his blog posts, podcast interviews, books, and TED talks, he recommends the same habit:&lt;/p&gt;

&lt;h2 id=&quot;become-an-idea-machine-by-writing-10-ideas-a-day&quot;&gt;Become an idea machine by writing 10 ideas a day.&lt;/h2&gt;

&lt;p&gt;You don’t need to come up with 10 brilliant ideas.&lt;/p&gt;

&lt;p&gt;You just need to come up with 10 bad ideas every day. You need to build your idea muscles. Yes, your idea muscles atrophy like any other muscle if you don’t use them.&lt;/p&gt;

&lt;p&gt;If you don’t know how to start, write 10 areas you want to write 10 ideas about.&lt;/p&gt;

&lt;p&gt;Write 10 ideas to:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Get a pay raise&lt;/li&gt;
  &lt;li&gt;Get a promotion&lt;/li&gt;
  &lt;li&gt;Find a better job&lt;/li&gt;
  &lt;li&gt;Make extra income&lt;/li&gt;
  &lt;li&gt;Expand your network&lt;/li&gt;
  &lt;li&gt;Ease your boss’s job&lt;/li&gt;
  &lt;li&gt;Land your next client&lt;/li&gt;
  &lt;li&gt;Help your best client&lt;/li&gt;
  &lt;li&gt;Grow your online presence&lt;/li&gt;
  &lt;li&gt;Improve your team’s processes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After following this habit for weeks, out of those hundreds of ideas, one will be a decent one. Then, write 10 ideas to execute on that idea.&lt;/p&gt;

&lt;h2 id=&quot;an-idea-machine-writes-ideas-for-others-too-and-gives-them-away-without-expecting-anything-inreturn&quot;&gt;An idea machine writes ideas for others too and gives them away without expecting anything in return.&lt;/h2&gt;

&lt;p&gt;Months later after starting to write and give away ideas, James was flying to visit companies and discuss the ideas he gave for free.&lt;/p&gt;

&lt;p&gt;After learning about becoming an idea machine, I gave 10 ideas on a LinkedIn comment to a coding non-profit running out of cash. I also DMed the CEO of a SaaS to give him my ideas for his landing page and product documentation. I’m still waiting for my free plane tickets to discuss my ideas though.&lt;/p&gt;

&lt;p&gt;Want to change your life? Start writing 10 ideas every day and become an idea machine.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Day I Realized I Needed to Raise My Rates as a Coder</title>
   <link href="https://canro91.github.io/2024/11/04/RaisingRates/"/>
   <updated>2024-11-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/04/RaisingRates</id>
   <content type="html">&lt;p&gt;Hourly billing is nuts.&lt;/p&gt;

&lt;p&gt;I learned that from &lt;a href=&quot;https://jonathanstark.com/&quot;&gt;Jonathan Starks&lt;/a&gt;. Charging by the hour doesn’t encourage productivity. To make more money, you have to work slowly or pad your time sheet. In either case, you’re not being honest.&lt;/p&gt;

&lt;p&gt;Even knowing that, I agreed to work as a contractor for a small software agency charging by the hour.&lt;/p&gt;

&lt;p&gt;After every task, I couldn’t resist the temptation of looking at my tracking software to see how much I had made in that working session. “I’ve made only $75, I need to pay my bills. Arrrggg!”&lt;/p&gt;

&lt;h2 id=&quot;but-that-story-started-months-earlier-after-being-let-go&quot;&gt;But that story started months earlier, after being “let go.”&lt;/h2&gt;

&lt;p&gt;After the initial moments of relief from a layoff, the anxiety of not having a paycheck made me go into panic mode.&lt;/p&gt;

&lt;p&gt;I sent my CV to as many places looking for coding skills as possible. &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;Even to a FAANG&lt;/a&gt;, and my overconfidence and lack of preparation made failed.&lt;/p&gt;

&lt;p&gt;Out of dozens of applications, only one got me a reply: “Thanks, but we’re not looking. Maybe later.” A polite no.&lt;/p&gt;

&lt;h2 id=&quot;months-later-they-wanted-to-chat-with-me&quot;&gt;Months later, they wanted to chat with me.&lt;/h2&gt;

&lt;p&gt;I wasn’t used to charging by the hour.&lt;/p&gt;

&lt;p&gt;As a full-time employee, you receive your monthly paycheck and don’t run those calculations. Maybe HR does, not you. I picked a rate out of thin air based on my last salary.&lt;/p&gt;

&lt;p&gt;One ex-coworker suggested that I should go with a lower rate since hiring in 2024 has been slow. AI, high interest rates, or who knows what made the demand for software engineers decrease.&lt;/p&gt;

&lt;p&gt;I followed that advice and lowered my rate.&lt;/p&gt;

&lt;h2 id=&quot;the-day-of-the-interview-came&quot;&gt;The day of the interview came.&lt;/h2&gt;

&lt;p&gt;The usual questions: “what do you do?” and “tell me about yourself.” And of course, my hourly rate.&lt;/p&gt;

&lt;p&gt;When I asked about my future project, I was told it was an app migration from an ancient tech stack to a newer one and they were looking for someone “cheap” like me to handle the repetitive part.&lt;/p&gt;

&lt;p&gt;Ouch! That hurt. Directly in my ego. “Thanks for being honest,” I thought.&lt;/p&gt;

&lt;p&gt;I knew I needed to set an aspirational rate (lesson from Naval Ravikant) and escape from the Matrix. That moment reinforced that decision. But my emergency fund was about to run out in a couple of months, so I had to swallow my pride and be the “cheap” one.&lt;/p&gt;

&lt;h2 id=&quot;how-i-should-have-picked-my-rates&quot;&gt;How I should have picked my rates&lt;/h2&gt;

&lt;p&gt;The next time you’re asked about your rates, go with the advice from “The Secrets of Consulting” by Gerald Weinberg:&lt;/p&gt;

&lt;p&gt;Give a rate that makes you comfortable either if it’s accepted or not. Avoid the “oh no, I should have charged more” and the “damn, I charged too high.”&lt;/p&gt;

&lt;p&gt;Or use a more practical approach:&lt;/p&gt;

&lt;p&gt;Slap the other person in the face and say your rate. If he gets offended by the slap, you’re charging too low.&lt;/p&gt;

&lt;p&gt;Anyway, like me, you probably need to raise your rates too.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>If a Language Learning Forum Were Like a Coding Forum:</title>
   <link href="https://canro91.github.io/2024/11/03/CodingForum/"/>
   <updated>2024-11-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/03/CodingForum</id>
   <content type="html">&lt;p&gt;Question: Guys, how do I say “I love you” in Japanese?&lt;/p&gt;

&lt;p&gt;— Japanese? Why don’t you try German? It’s easier in German&lt;/p&gt;

&lt;p&gt;— If you want to talk about love, you should try with French&lt;/p&gt;

&lt;p&gt;— Yeah, go with French. That’s the language of love&lt;/p&gt;

&lt;p&gt;…Question downvoted&lt;/p&gt;

&lt;p&gt;— Wait, isn’t Spanish the language of love?&lt;/p&gt;

&lt;p&gt;— Guys, the thing is I have this text at work and I need to say that in Japanese&lt;/p&gt;

&lt;p&gt;— Really? What kind of job is that? You should talk to your boss and tell him to use a real language for that&lt;/p&gt;

&lt;p&gt;— Do you call yourself a language lover and don’t know that?&lt;/p&gt;

&lt;p&gt;…Question closed as duplicate.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Passive Learning Is Just Entertainment Unless You Follow These Two Strategies</title>
   <link href="https://canro91.github.io/2024/11/02/PassiveLearning/"/>
   <updated>2024-11-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/02/PassiveLearning</id>
   <content type="html">&lt;p&gt;Passive consumption is procrastination in disguise.&lt;/p&gt;

&lt;p&gt;If you’re like me, you’ve been watching another YouTube video, reading another book, and consuming online content as procrastination.&lt;/p&gt;

&lt;p&gt;Mindless consumption isn’t learning.&lt;/p&gt;

&lt;p&gt;Learning should be active. We truly learn when we engage in our learning process. Instead of simply reading or watching pretending to learn, engage with what we’re learning.&lt;/p&gt;

&lt;h2 id=&quot;start-taking-notes-by-hand&quot;&gt;Start taking notes by hand&lt;/h2&gt;

&lt;p&gt;That’s the easiest way to engage with your learning.&lt;/p&gt;

&lt;p&gt;Yes, taking notes by hand! When we take notes by hand, we get “physical” like the 80s hit. Touching a pen and feeling the paper create brain connections that reinforce our learning.&lt;/p&gt;

&lt;p&gt;Ditch your laptop and go back to pen and paper.&lt;/p&gt;

&lt;h2 id=&quot;after-taking-notes-create-something-new&quot;&gt;After taking notes, create something new&lt;/h2&gt;

&lt;p&gt;Taking notes is only the first step.&lt;/p&gt;

&lt;p&gt;The best way to truly learn is to create something from what you consume.&lt;/p&gt;

&lt;p&gt;Instead of reading to show off a book count, &lt;a href=&quot;/2024/05/13/HowToReadNonFictionBooks/&quot;&gt;read with intention&lt;/a&gt;: answer a question or face a challenge. Once you read, write a summary or explain it to others in your own words.&lt;/p&gt;

&lt;p&gt;If you’re a podcast lover, don’t simply listen to the next episode until the end. Write down 3 main points you learned, transcribe your handwritten notes, and share them online.&lt;/p&gt;

&lt;p&gt;The next time you find a new programming concept, incorporate that concept into a small project. And, if you’re reading an open-source project, create a simplified version of its main feature to make sure you understand the main decisions and concepts behind it.&lt;/p&gt;

&lt;p&gt;And, if you like writing, do copy work. Recreate a piece from a writer you want to imitate. Again by hand. This is an exercise attributed to Benjamin Franklin. After reading a piece, he looked away and tried to rewrite what he read.&lt;/p&gt;

&lt;p&gt;Separate the exploration phase where you only gather resources from the engaging phase where you create something new after consuming.&lt;/p&gt;

&lt;p&gt;If you listen, you will forget. If you write, you will remember. But if you practice, you won’t forget.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>It&apos;s Not About Selling. It&apos;s About Helping</title>
   <link href="https://canro91.github.io/2024/11/01/SellingVsHelping/"/>
   <updated>2024-11-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/11/01/SellingVsHelping</id>
   <content type="html">&lt;p&gt;The other day I had a weird DM exchange.&lt;/p&gt;

&lt;p&gt;A guy shared a cool LinkedIn tip. I rushed to apply it and DMed him to say thanks for that cool idea. You know I wanted to make friends in the DM.&lt;/p&gt;

&lt;p&gt;He continued the conversation. I shared how my LinkedIn journey was going. I told him about my 100-post experiment and my future plans.&lt;/p&gt;

&lt;p&gt;After a couple of exchanges, the guy went on sale mode. He had an offering and I could potentially be one client. I got that!&lt;/p&gt;

&lt;p&gt;But, he wrote something in the lines of &lt;em&gt;“Truth is I don’t need more clients and I’m fully booked…“&lt;/em&gt; I stopped reading at that point. If he doesn’t need clients, why he was on salesy mode with that line in the first place?&lt;/p&gt;

&lt;p&gt;People don’t like those mind tricks. At least I don’t. Those Jedi mind tricks don’t work on me.&lt;/p&gt;

&lt;p&gt;I would have rephrased that message like &lt;em&gt;“hey, maybe you don’t help with &amp;lt;insert service here&amp;gt; yet. Once you do, feel free to shoot me a message, I’d be glad to help you. That’s what I do for a living and I have helped more than &amp;lt;insert client count here&amp;gt; people with that.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It would have booked a call after a message like that. Or at least I wouldn’t have ghosted him.&lt;/p&gt;

&lt;p&gt;It’s not about selling, it’s about helping.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Power of Function Composition — to Find If an Array Is Special</title>
   <link href="https://canro91.github.io/2024/10/30/PowerOfComposition/"/>
   <updated>2024-10-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/10/30/PowerOfComposition</id>
   <content type="html">&lt;p&gt;Passing the result of a function to another isn’t the only way to compose two functions.&lt;/p&gt;

&lt;p&gt;From math classes, we’re used to composing two functions like this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compose(f, g) = x =&amp;gt; f(g(x))&lt;/code&gt;. But it turns out there are more ways.&lt;/p&gt;

&lt;p&gt;That’s my main takeaway from the talk “The Power of Function Composition” by Conor Hoekstra at NDC Conference 2024.&lt;/p&gt;

&lt;p&gt;Here’s the YouTube recording,&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/fuX4bQefvWQ?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;The talk shows how to find if an array is special using languages like Python, Haskell, and other obscure and esoteric ones to show function composition.&lt;/p&gt;

&lt;p&gt;These are the lessons I learned, translating some code examples to C#.&lt;/p&gt;

&lt;h2 id=&quot;a-simple-solution-to-find-if-an-array-is-special&quot;&gt;A simple solution to find if an array is special&lt;/h2&gt;

&lt;p&gt;Let’s find if an array is special.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;An array is special if every pair of consecutive elements contains two numbers with different parity. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[4,3,1,6]&lt;/code&gt; isn’t special. It has three pairs: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4,3&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3,1&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1,6&lt;/code&gt;. The second pair has two odd numbers, which makes our array “not special.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s the &lt;a href=&quot;https://leetcode.com/problems/special-array-i/description/&quot;&gt;LeetCode problem&lt;/a&gt; if you want to try it yourself.&lt;/p&gt;

&lt;p&gt;And here’s my solution before using any of the new concepts from the talk,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Or simply:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// return array.Zip(array.Skip(1));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HasDifferentParity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasDifferentParity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pairs()&lt;/code&gt; to generate the pairs of consecutive elements, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasDifferentParity()&lt;/code&gt; to find if both numbers in a pair are even or odd, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;All()&lt;/code&gt; from LINQ. Nothing fancy!&lt;/p&gt;

&lt;h2 id=&quot;apart-from-the-usual-composition-there-are-other-ways-of-composing-functions&quot;&gt;Apart from the “usual” composition, there are other ways of composing functions&lt;/h2&gt;

&lt;p&gt;Here are some of them, in pseudo-code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def i(x) = x
def k(x, y) = x
def ki(x, y) = y

def s(f, g) = \x =&amp;gt; f(x, g(x))
def b(f, g) = \x =&amp;gt; f(g(x)) // &amp;lt;--

def c(f) = \x, y =&amp;gt; f(y, x)
def w(f) = \x =&amp;gt; f(x, x)

def d(f, g) = \x, y =&amp;gt; f(x, g(y))
def b1(f, g) = \x, y =&amp;gt; f(g(x, y))
def psi(f, g) = \x, y =&amp;gt; f(g(x), g(y)) // &amp;lt;--
def phi(f, g, h) = \x =&amp;gt; g(f(x), h(x))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s take a close look at two of them.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt; is the composition we all know. It passes the result of the second function to the first one. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psi&lt;/code&gt; passes two values to the second function and then calls the first one with both results.&lt;/p&gt;

&lt;h2 id=&quot;lets-find-if-an-array-is-special-again-but-composing-functions&quot;&gt;Let’s find if an array is special again, but composing functions&lt;/h2&gt;

&lt;p&gt;Let’s revisit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsSpecial()&lt;/code&gt; but using “psi” this time,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsEven&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// A more general signature would be:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Func&amp;lt;T1,T1,T3&amp;gt; Psi&amp;lt;T1, T2, T3&amp;gt;(Func&amp;lt;T2,T2,T3&amp;gt; f, Func&amp;lt;T1,T2&amp;gt; g)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// But to make things easier:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Psi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                   ^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasDifferentParity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Psi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsEven&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hasDifferentParity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pairs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Psi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsEven&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                          ^^^&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasDifferentParity()&lt;/code&gt;, it uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Psi(NotEqual, IsEven)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And again, here’s the pseudo-code of psi: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psi(f, g) = \x, y =&amp;gt; f(g(x), g(y))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Psi(NotEqual, IsEven)&lt;/code&gt; receives two integers, calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsEven&lt;/code&gt; with each of them, and passes both results to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NotEqual&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s a new way of composition.&lt;/p&gt;

&lt;h2 id=&quot;lets-do-it-the-haskell-way-using-mapadjacent&quot;&gt;Let’s do it the Haskell way using MapAdjacent&lt;/h2&gt;

&lt;p&gt;From &lt;a href=&quot;https://hackage.haskell.org/package/utility-ht-0.0.17.2/docs/Data-List-HT.html&quot;&gt;Haskell official docs&lt;/a&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapAdjacent&lt;/code&gt; transforms every pair of consecutive elements.&lt;/p&gt;

&lt;p&gt;Here’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsSpecial()&lt;/code&gt; one more time, but using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapAdjacent()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Pairs, IsEven, NotEqual, and Psi are the same as before...&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEven&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapAdjacent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;And&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapAdjacent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Psi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsEven&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;And&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;IsSpecial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It transforms our array into an array of booleans–if every number is even–with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select()&lt;/code&gt;, then finds if every pair of booleans is different with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapAdjacent()&lt;/code&gt; and lastly collapses all the results with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;And()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As an alternative, it uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapAdjacent()&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Psi(NotEqual, IsEven)&lt;/code&gt; to avoid using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select()&lt;/code&gt; first. This alternative is closer to the previous one with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pairs()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;All()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we don’t have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapAdjancent()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;And()&lt;/code&gt;, here are their C# versions,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Enumerable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MapAdjacent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;And&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapAdjacent()&lt;/code&gt; is a wrapper of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Zip()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;And()&lt;/code&gt; is a wrapper of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;All()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is one of the talks to replay multiple times if you want to grasp its concepts, since they’re not that common. Also, it covers the same example in other less known languages that I don’t even remember their names.&lt;/p&gt;

&lt;p&gt;Since you won’t use those obscure languages, learn the composition part of the talk. It’s mind blowing. In any case, if you want to start with Functional Programming, you’ll need &lt;a href=&quot;/2024/10/29/FPOnDotNet/&quot;&gt;simpler concepts&lt;/a&gt;.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Using Lambda Expressions Doesn&apos;t Make Your C# Code Functional</title>
   <link href="https://canro91.github.io/2024/10/29/FPOnDotNet/"/>
   <updated>2024-10-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/10/29/FPOnDotNet</id>
   <content type="html">&lt;p&gt;C# will never become a truly functional language.&lt;/p&gt;

&lt;p&gt;Sure, C# is borrowing features from functional languages like records, pattern matching, and switch expressions. But it doesn’t make it a functional language.&lt;/p&gt;

&lt;p&gt;At its core, C# is an Object Oriented language — with mutable state baked in. But it doesn’t mean we can make it functional with a few tweaks.&lt;/p&gt;

&lt;p&gt;That’s the main takeaway from the talk “Functional Programming on .NET” by Isaac Abraham at NDC Conference 2024.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/V9GYPOsPj4M?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Here are some other lessons I learned from that talk.&lt;/p&gt;

&lt;h2 id=&quot;here-are-two-misconceptions-about-functional-programming&quot;&gt;Here are two misconceptions about Functional Programming&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. “It’s difficult”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we hear “Functional Programming” what comes to mind is obscure jargon like monad, monoids, and endofunctors. They sound like you have to grow a beard to understand them. That scares programmers away from Functional Programming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. “I’m already using lambda expressions”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lambda expressions and other recent features are syntactic sugar to make C# less verbose. Lambda expressions simplify delegates. And we’ve had delegates since C# 1.0 when C# looked way less functional than today.&lt;/p&gt;

&lt;p&gt;The truth is functional programming could be simpler and lambda expressions don’t make C# functional.&lt;/p&gt;

&lt;h2 id=&quot;if-lambda-expressions-and-other-features-dont-make-c-functional-what-is-it-then&quot;&gt;If lambda expressions and other features don’t make C# functional, what is it then?&lt;/h2&gt;

&lt;p&gt;Expressions and Immutability.&lt;/p&gt;

&lt;p&gt;Expressions mean everything has a result. Think of old-style switch/case statements vs switch expressions. We can assign the result of a switch expression to a variable, but not an old-style switch/case. If it can be assigned to the left of =, it’s an expression.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This is not an expression&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Visa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Mastercard&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MasterCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unknown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This is an expression&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;Visa&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;MasterCard&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MasterCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unknown&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And immutability means that everything is set for life. Think of records, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt; keyword, and private setters.&lt;/p&gt;

&lt;p&gt;With these two concepts, to make C# functional, we should forget about classes and inheritance to go with static methods. We should forget constructor injection and go with regular method parameters. And we should separate data from behavior instead of keeping them side by side in a class that mutates state.&lt;/p&gt;

&lt;p&gt;Following these two concepts, a dummy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Calculator&lt;/code&gt; that looks like this&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Calculator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Adding &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;will look like this instead&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FunctionalishCalculator&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Adding &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And in F#, it will look like this&lt;/p&gt;

&lt;div class=&quot;language-fsharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Adding {a}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A few lines of code. In F#, functions don’t have to live in classes. The compiler infers parameters types. And we don’t need to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return&lt;/code&gt; keyword.&lt;/p&gt;

&lt;h2 id=&quot;the-perfect-example-of-expressions-and-immutability-in-practice-is-linq&quot;&gt;The perfect example of expressions and immutability in practice is LINQ.&lt;/h2&gt;

&lt;p&gt;LINQ is functional.&lt;/p&gt;

&lt;p&gt;Every LINQ method returns a new collection. We don’t have “void” LINQ methods. Expressions. And no LINQ methods change the input collection. Immutability.&lt;/p&gt;

&lt;p&gt;That’s why LINQ is the best of all C# features—It’s functional.&lt;/p&gt;

&lt;p&gt;Voilà! We don’t need fancy features like &lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;discriminated unions&lt;/a&gt; to adopt functional ideas in C#. Only those two fundamental concepts. Sure, new features help a lot!&lt;/p&gt;

&lt;p&gt;Start by &lt;a href=&quot;/2024/08/05/IOToTheEdges/&quot;&gt;keeping IO away from your core, at the edges&lt;/a&gt;. And go with expressions and immutability.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>14 Quick Career Lessons After 10+ Years (and Lots of Trial and Error) as a Software Engineer</title>
   <link href="https://canro91.github.io/2024/10/21/QuickCareerLessons/"/>
   <updated>2024-10-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/10/21/QuickCareerLessons</id>
   <content type="html">&lt;p&gt;Coding is the easy part of software engineering.&lt;/p&gt;

&lt;p&gt;We don’t get an instruction manual to survive the corporate world and navigate the “people and interactions” side of software engineering.&lt;/p&gt;

&lt;p&gt;After over 10 years, 3 jobs, 2 “we have to let you go,” and lost of trial and error, these are 14 lessons I’ve learned while navigating a career as a software engineer:&lt;/p&gt;

&lt;h2 id=&quot;1-you-can-lose-your-job-at-any-time&quot;&gt;1. You Can Lose Your Job At Any Time&lt;/h2&gt;

&lt;p&gt;You can’t control it. A pandemic, a recession, or anything else. But you can control: your online presence, emergency fund, and side income.&lt;/p&gt;

&lt;h2 id=&quot;2-detach-your-sense-of-meaning-from-your-work&quot;&gt;2. Detach Your Sense Of Meaning From Your Work&lt;/h2&gt;

&lt;p&gt;Work gives value and meaning.&lt;/p&gt;

&lt;p&gt;But you’re not your work. You’re the books you’ve read, places you’ve visited, and people you’ve met. You’re your connections, experiences, and knowledge.&lt;/p&gt;

&lt;p&gt;You’re more than a job.&lt;/p&gt;

&lt;h2 id=&quot;3-build-multiple-sources-of-income&quot;&gt;3. Build Multiple Sources Of Income&lt;/h2&gt;

&lt;p&gt;A rental property, stock portfolio, or digital products. You name it.&lt;/p&gt;

&lt;p&gt;Don’t rely only on your salary and don’t live paycheck to paycheck. There’s no safe job. See #1.&lt;/p&gt;

&lt;h2 id=&quot;4-always-be-ready-to-leave-your-current-job&quot;&gt;4. Always Be Ready To Leave Your Current Job&lt;/h2&gt;

&lt;p&gt;If you wait for a layoff to take action, it’s already too late.&lt;/p&gt;

&lt;p&gt;Grow your network, &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;have your CV updated&lt;/a&gt;, and keep your “tell me about yourself” muscles in shape. Yes, hiring is broken.&lt;/p&gt;

&lt;h2 id=&quot;5-change-jobs-often&quot;&gt;5. Change Jobs Often&lt;/h2&gt;

&lt;p&gt;Or at least, don’t stay too long at stagnant jobs.&lt;/p&gt;

&lt;p&gt;Otherwise, your CV will become outdated, your interviewing skills will get rusty, and you will leave money on the table.&lt;/p&gt;

&lt;h2 id=&quot;6-climbing-the-corporate-ladder-is-a-myth&quot;&gt;6. Climbing The Corporate Ladder Is A Myth&lt;/h2&gt;

&lt;p&gt;Anyone can add or remove steps in the ladder. Or remove the entire ladder. The best ladder to climb is the one you build for yourself.&lt;/p&gt;

&lt;p&gt;A new title comes with more meetings, extra hours, and the same salary. Instead of optimizing for a title, optimize for a lifestyle.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;7-vacations-dont-change-an-unfulfilling-job&quot;&gt;7. Vacations Don’t Change An Unfulfilling Job&lt;/h2&gt;

&lt;p&gt;Neither do pay raises.&lt;/p&gt;

&lt;p&gt;They only move a “death sentence” a couple of months ahead.&lt;/p&gt;

&lt;h2 id=&quot;8-you-dont-have-to-feel-miserable&quot;&gt;8. You Don’t Have To Feel Miserable&lt;/h2&gt;

&lt;p&gt;If you don’t feel like getting out of bed to work at that place, make a change. Find a way to motivate yourself and keep learning. Or look for “greener pastures.”&lt;/p&gt;

&lt;h2 id=&quot;9-you-dont-get-burned-out-by-doing-too-much&quot;&gt;9. You Don’t Get Burned Out By Doing Too Much&lt;/h2&gt;

&lt;p&gt;But by doing too little of the things you care about. (Jim Kwik, brain coach)&lt;/p&gt;

&lt;p&gt;And seek help when people around you start to be affected by your burnout.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;10-software-projects-dont-fail-because-of-programming-languages-and-tech-stacks&quot;&gt;10. Software Projects Don’t Fail Because Of Programming Languages And Tech Stacks&lt;/h2&gt;

&lt;p&gt;Nope! They fail because of poor communication and unclear expectations. The same as marriages. Even unclear expectations are a communication issue.&lt;/p&gt;

&lt;h2 id=&quot;11-dont-be-a-hero-be-a-team-player&quot;&gt;11. Don’t Be A Hero. Be A Team Player&lt;/h2&gt;

&lt;p&gt;If you are the only one who knows or does something in your team, you’re being a hero.&lt;/p&gt;

&lt;p&gt;It feels great when you are the one who saves the day, but being a hero is a trap. A hero can’t get sick, go on vacation, or be promoted. Be a team player instead.&lt;/p&gt;

&lt;h2 id=&quot;12-the-minute-you-learn-something-teach-it&quot;&gt;12. The Minute You Learn Something, Teach It&lt;/h2&gt;

&lt;p&gt;That’s from &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt; by Austin Kleon.&lt;/p&gt;

&lt;h2 id=&quot;13-dont-ask-someone-whos-leaving-to-finish-an-important-task-at-work&quot;&gt;13. Don’t Ask Someone Who’s Leaving To Finish An Important Task At Work&lt;/h2&gt;

&lt;p&gt;As soon as he leaves, something will happen to the task he finished: an unexpected issue, a change in requirements, or a new scenario nobody saw coming. And he will be the only one who knows how to handle it. Too late. He’s already gone.&lt;/p&gt;

&lt;h2 id=&quot;14-the-more-senior-you-get-the-less-its-about-coding&quot;&gt;14. The More Senior You Get, The Less It’s About Coding&lt;/h2&gt;

&lt;p&gt;There’s more than coding:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Team dynamics&lt;/li&gt;
  &lt;li&gt;Project management&lt;/li&gt;
  &lt;li&gt;Inter-team communication&lt;/li&gt;
  &lt;li&gt;Scope and deadline negotiations&lt;/li&gt;
  &lt;li&gt;Managing stakeholders’ expectations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s more than only source code in a text editor.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;Hoping and praying without taking action is a bad career strategy. Instead, define what you want from your career. Money? Connections? Recognition? Create a career plan that reflects that, pick the right jobs, and always have an escape plan.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Your career is your responsibility, not your employer’s”&lt;/em&gt; — &lt;a href=&quot;/2020/06/15/CleanCoder/&quot;&gt;Clean Coder&lt;/a&gt; by Uncle Bob.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Learning a Second Language Could Be Your Best Career Decision — Start With These 7 Steps</title>
   <link href="https://canro91.github.io/2024/10/14/LearningLanguages/"/>
   <updated>2024-10-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/10/14/LearningLanguages</id>
   <content type="html">&lt;p&gt;Learning a second language has been my best career decision.&lt;/p&gt;

&lt;p&gt;Literally, it changed my life and doubled my salary.&lt;/p&gt;

&lt;p&gt;Initially, I learned English to make my &lt;a href=&quot;/2024/10/07/TipsToWriteBetterCVs/&quot;&gt;CV look more attractive&lt;/a&gt; to recruiters. But learning English has been more than a better CV for me.&lt;/p&gt;

&lt;p&gt;It took me a couple of years to learn English and an extra couple of years to take full advantage of it. I went to a traditional language school: lots of grammar exercises and “repeat after me” sessions.&lt;/p&gt;

&lt;p&gt;With the right method and consistency, you could learn a language faster. One year is a good time frame to reach a conversational level. Of course, your mileage may vary.&lt;/p&gt;

&lt;p&gt;If you’re learning a second language to boost your career, prepare for your next vacation abroad, or connect with your extended family, follow these 7 proven steps:&lt;/p&gt;

&lt;h2 id=&quot;1-fluency-isnt-perfection&quot;&gt;1. Fluency Isn’t Perfection&lt;/h2&gt;

&lt;p&gt;Before learning a single word or grammar rule, understand that nobody speaks any language perfectly.&lt;/p&gt;

&lt;p&gt;I don’t speak perfect English. I don’t even speak perfectly in my native language. Nobody speaks perfectly in any language.&lt;/p&gt;

&lt;p&gt;Speaking a language isn’t about making zero mistakes. It’s about communication and human connection.&lt;/p&gt;

&lt;p&gt;Fluency isn’t perfection.&lt;/p&gt;

&lt;p&gt;With that myth debunked, let’s move to the next step.&lt;/p&gt;

&lt;h2 id=&quot;2-identify-your-goals&quot;&gt;2. Identify Your Goals&lt;/h2&gt;

&lt;p&gt;Start your language journey by identifying your goal and your time frame.&lt;/p&gt;

&lt;p&gt;A clear goal helps you determine what skills you need to focus on and what vocabulary to master. Studying to pass a language certification exam in a few months is different from learning useful phrases for your next vacation.&lt;/p&gt;

&lt;p&gt;To keep that goal in mind, set a language checkpoint or milestone. For example, your first checkpoint could be a 15-minute conversation with a native speaker after your first month of practice.&lt;/p&gt;

&lt;h2 id=&quot;3-find-a-learning-method&quot;&gt;3. Find a Learning Method&lt;/h2&gt;

&lt;p&gt;The key to learning a new language is to tie it to activities you enjoy doing.&lt;/p&gt;

&lt;p&gt;I enjoy listening to podcasts and watching videos. When I started to learn French, I found a podcast for beginners and followed it during my lunch breaks. While everybody else at the office was taking a nap, I was with my headphones on, listening and taking notes.&lt;/p&gt;

&lt;p&gt;From Scott Young’s &lt;a href=&quot;/2020/07/14/UltralearningTakeaways/&quot;&gt;Ultralearning book&lt;/a&gt;, the guy behind the “One Year Without English” challenge, I learned to spend about 10% of the learning time finding resources and study guides.&lt;/p&gt;

&lt;p&gt;Spend the first days, looking up good resources to learn your target language.&lt;/p&gt;

&lt;h2 id=&quot;4-find-a-teacher-or-a-language-partner&quot;&gt;4. Find a Teacher or a Language Partner&lt;/h2&gt;

&lt;p&gt;No matter how shy you think you are, sooner or later you will have to open your mouth and let words out.&lt;/p&gt;

&lt;p&gt;My best strategy is to find a teacher or language partner for one-to-one sessions. Personally, I have used iTalki to find my teachers and language partners. Some teachers offer structured lesson plans or only conversation practice. But with language partners, expect only chit-chat about anything. Usually, you split the practice session in half: switching languages each time.&lt;/p&gt;

&lt;p&gt;I have to confess I’ve been lucky with my language partners. After several practicing sessions with a language partner, we became friends and met in real life. It happened we were on vacation in the same city. And, thanks to those unexpected life turns, my language partner’s husband was a lawyer and ended up helping me review my contracts before joining my first job at an international software agency.&lt;/p&gt;

&lt;p&gt;A teacher or language partner will help you practice in a stress-free environment.&lt;/p&gt;

&lt;p&gt;If you’re learning a language for work, ask them to role-play being your interviewer or a participant at a meeting you have to conduct.&lt;/p&gt;

&lt;h2 id=&quot;5-practice-a-little-bit-every-day&quot;&gt;5. Practice a Little Bit Every Day&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Little by little, a little becomes a lot.” — Jim Kwik, the Brain Coach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to see fast results, study a bit every day. Learn 5 phrases or listen to a short audio a day. That’s better than a long study session once a week.&lt;/p&gt;

&lt;p&gt;If you think you don’t have time, take advantage of every dead time slot in your schedule: while waiting in lines, on the bus, or walking home.&lt;/p&gt;

&lt;p&gt;Back in the day when I had to commute, I reviewed my Anki flashcards and listened to podcasts on my way home.&lt;/p&gt;

&lt;p&gt;If you’re not used to practicing a language daily, tie your study or practice sessions to an already-built habit. Do you have to do the dishes? The laundry? Listen to an audio lesson while doing it. You said you don’t have already built habits? What about listening to podcasts while in the shower? You shower regularly, right?&lt;/p&gt;

&lt;h2 id=&quot;6-learn-phrases-instead-of-words&quot;&gt;6. Learn Phrases Instead of Words&lt;/h2&gt;

&lt;p&gt;Don’t memorize words or grammar rules.&lt;/p&gt;

&lt;p&gt;I know it’s frustrating when your teacher asks you to open a textbook to work on grammar rules in your first class. It kills all the fun of learning a language.&lt;/p&gt;

&lt;p&gt;Instead of learning individual words, learn complete phrases and use them as LEGO bricks you assemble and interchange. You will learn words in context along with their gender. Yes, words have gender in other languages. And it’s funny when one word has a different gender in your target language.&lt;/p&gt;

&lt;p&gt;Also, don’t overthink grammar rules or find logic in your target language.&lt;/p&gt;

&lt;p&gt;When we were children, we didn’t receive a grammar book to learn our native languages. We absorbed grammar rules after being exposed to the language for hours and hours.&lt;/p&gt;

&lt;h2 id=&quot;7-immerse-yourself-in-the-language&quot;&gt;7. Immerse Yourself in the Language&lt;/h2&gt;

&lt;p&gt;This is the #1 tip to study from home – Create a language environment around you and your hobbies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Change your phone and computer language&lt;/li&gt;
  &lt;li&gt;Read the news or watch TV&lt;/li&gt;
  &lt;li&gt;Follow social media&lt;/li&gt;
  &lt;li&gt;Listen to music&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You name it!&lt;/p&gt;

&lt;p&gt;You don’t have to travel abroad to learn a language, you can do it in the comfort of your bed.&lt;/p&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting Thoughts&lt;/h2&gt;

&lt;p&gt;Thanks to learning a new language, I’ve made good friends, doubled my salary, and discovered a passion for learning and teaching.&lt;/p&gt;

&lt;p&gt;We become a new person with every language we learn. I feel like I’m more extroverted when speaking English.&lt;/p&gt;

&lt;p&gt;I have found that there’s no better way to build trust with coworkers or clients at work than to say hi, thanks, yes/no, and bye in their target language. It will bring smiles to their faces and show you’re interested in their culture.&lt;/p&gt;

&lt;p&gt;So if you’re looking for a twist in your career, try a new language. Try Arabic to work in the oil industry, German for the automotive industry, or Spanish to join a rising startup in Latin America…who knows!&lt;/p&gt;

&lt;p&gt;A second language could change your life too.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m Moving Monday Links to an Email List</title>
   <link href="https://canro91.github.io/2024/10/10/EmailList/"/>
   <updated>2024-10-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/10/10/EmailList</id>
   <content type="html">&lt;p&gt;Bye, Monday Links. Hello, Friday Links.&lt;/p&gt;

&lt;p&gt;For months, I ran a series called &lt;a href=&quot;/tags/mondaylinks/&quot;&gt;Monday Links&lt;/a&gt;, here on my blog. Every time I found interesting posts about programming and software engineering, I compiled them until I had five or six and then published them in a post.&lt;/p&gt;

&lt;p&gt;It was a way to document what I read and react to the other people’s posts, when my reaction didn’t merit a separate post. Think of Twitter/X threads on my blog.&lt;/p&gt;

&lt;p&gt;I’m moving my Monday Links to an email list.&lt;/p&gt;

&lt;p&gt;The format is almost the same. Every Friday, I send four or five posts about software engineering and programming. But I sprinkle other interesting subjects in between. Also, I include one of my own posts published the same week and share updates about the projects I’m creating.&lt;/p&gt;

&lt;p&gt;For example, here’s &lt;a href=&quot;/2024/02/19/MondayLinks/&quot;&gt;Monday Links on Notebooks, Learning, and KPIs&lt;/a&gt;, the last Monday Link I wrote here.&lt;/p&gt;

&lt;p&gt;If you have already downloaded any of my ebooks from &lt;a href=&quot;https://imcsarag.gumroad.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my Gumroad page&lt;/a&gt;, you’re already receiving my emails.&lt;/p&gt;

&lt;p&gt;But if you haven’t and want to receive them, &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;subscribe here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to reply to my Friday Links and comment or ask questions.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I Hope You Don&apos;t Have To Write a CV. But if You Do, Follow These Tips</title>
   <link href="https://canro91.github.io/2024/10/07/TipsToWriteBetterCVs/"/>
   <updated>2024-10-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/10/07/TipsToWriteBetterCVs</id>
   <content type="html">&lt;p&gt;CVs are so last century.&lt;/p&gt;

&lt;p&gt;I hope I don’t have to write a CV anymore. I hope you don’t either.&lt;/p&gt;

&lt;p&gt;A personal brand is the new CV and portfolio.&lt;/p&gt;

&lt;p&gt;But since you’re reading this one, probably you still need to write a CV. I wish it won’t end up in the dumpsters behind a company careers page. I’ve tried that route with zero results.&lt;/p&gt;

&lt;p&gt;More than once, some friend or ex-coworker has asked me to take a look at his CV. I’ve asked them too.&lt;/p&gt;

&lt;p&gt;I realized I had already given (and received) the same tips about CVs. These are five tips I always give to write better CVs as software engineers.&lt;/p&gt;

&lt;h2 id=&quot;1-keep-your-cv-to-only-one-page&quot;&gt;1. Keep Your CV to Only One Page&lt;/h2&gt;

&lt;p&gt;These days of endless scrolling on TikTok, nobody has time and attention to read more than that.&lt;/p&gt;

&lt;p&gt;Make it easy for people to review your CV. More than two pages are too long.&lt;/p&gt;

&lt;p&gt;Imagine your CV is a business card in a A4 page. You only have one or two minutes to introduce yourself in a business card. Do the same in a CV.&lt;/p&gt;

&lt;p&gt;Keep font size, spaces, and borders consistent.&lt;/p&gt;

&lt;h2 id=&quot;2-dont-include-a-photo-marital-status-national-id-or-other-personal-information&quot;&gt;2. Don’t Include a Photo, Marital Status, National ID, or Other Personal Information&lt;/h2&gt;

&lt;p&gt;Only add your professional email and LinkedIn profile, or any other social media profile to showcase your expertise. GitHub, Medium, dev.to handles work too.&lt;/p&gt;

&lt;p&gt;I even skip phone numbers. You don’t want strangers calling or texting you in the middle of the night for a “life changing opportunity at a well-know company in the tech sector.”&lt;/p&gt;

&lt;p&gt;You will be asked for personal information once you’re hired, not before.&lt;/p&gt;

&lt;p&gt;And only include your city and country if you’re applying for a position that requires being in a particular location. Don’t put your home address either.&lt;/p&gt;

&lt;h2 id=&quot;3-dont-add-every-single-experience-since-you-started-working&quot;&gt;3. Don’t Add Every Single Experience Since You Started Working&lt;/h2&gt;

&lt;p&gt;Use your most recent experience. The last five years, for example.&lt;/p&gt;

&lt;p&gt;And, include only the experience relevant to the job you’re applying for. Don’t add the unrelated part-time job you had in your first year at university.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1602407294553-6ac9170b3ed0?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0NTQ1MzIyNg&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;CV, Resume and computer&quot; /&gt;

&lt;figcaption&gt;You see? Even the one in the picture has only one page. Photo by &lt;a href=&quot;https://unsplash.com/@joaoscferrao?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;João Ferrão&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/resume?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;4-dont-include-a-cloud-of-keywords-languages-or-library-names&quot;&gt;4. Don’t Include a Cloud of Keywords, Languages, or Library Names&lt;/h2&gt;

&lt;p&gt;Stars or years working with a language don’t mean expertise.&lt;/p&gt;

&lt;p&gt;Probably, you’re riding a bike since you’re 4 or 5, but it doesn’t give you a place in the Tour de France. It’s the same with years of experience with a language or tool. What does five years with XML mean?&lt;/p&gt;

&lt;p&gt;Instead, describe what you did and what accomplishments you made in your previous jobs. Use numbers in your job descriptions.&lt;/p&gt;

&lt;p&gt;For example, &lt;em&gt;“I wrote an order processing component that scaled up to X orders per month by…“&lt;/em&gt; instead of &lt;em&gt;“C#/ASP.NET Core/SQL Server.”&lt;/em&gt; Somebody reading your CV would wonder what you did with those tools.&lt;/p&gt;

&lt;p&gt;A bunch of names by themselves mean nothing.&lt;/p&gt;

&lt;h2 id=&quot;5-skip-fancy-lines-like-honest-hard-working-team-player&quot;&gt;5. Skip Fancy Lines Like “Honest, Hard-Working, Team Player”&lt;/h2&gt;

&lt;p&gt;Everybody uses the same buzzwords.&lt;/p&gt;

&lt;p&gt;“Team player,” “detail-oriented,” and “enthusiast” only take extra space in your single-page CV. Do you still remember tip 1, right? And guess what? Everybody desperate for a job is a detail-oriented enthusiast team player who like to work under pressure in their CV.&lt;/p&gt;

&lt;p&gt;Instead of fancy lines, describe in a paragraph what you do and how you can help your next employer.&lt;/p&gt;

&lt;p&gt;Voilà! Five tips as promised. Remember recruiters, technical managers, and CTOs will read your CV looking for different information, not only a cloud of languages and libraries.&lt;/p&gt;

&lt;p&gt;A CV is to start an interview process. Keep it short and to the point. Be clear. “If you confuse, you lose.”&lt;/p&gt;

&lt;p&gt;If you’re interested in more interview preparation material, check &lt;a href=&quot;/2019/09/29/RemoteInterviewTips/&quot;&gt;these interview types and tips&lt;/a&gt;, &lt;a href=&quot;/2024/05/27/ApplyingToFaang/&quot;&gt;three interviewing lessons after applying at a FAANG&lt;/a&gt;, and follow these &lt;a href=&quot;/2021/11/22/CodingChallengeTips/&quot;&gt;ten tips to solve your next take-home coding exercises&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to Use the Specification Pattern in C# to Simplify Repositories</title>
   <link href="https://canro91.github.io/2024/09/30/SpecificationPattern/"/>
   <updated>2024-09-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/09/30/SpecificationPattern</id>
   <content type="html">&lt;p&gt;Repositories are the least SOLID part of our codebases.&lt;/p&gt;

&lt;p&gt;When we work with Domain-Driven Design, we take care of our business domain and forget about our data-access layer. We end up dumping, in a single interface, every combination of methods and parameters to retrieve our entities from the database. This way, we break the Single Responsibility Principle and Interface Segregation Principle. You see? They’re the least SOLID part.&lt;/p&gt;

&lt;p&gt;Our repositories become so bloated that to use one specific method from a repository, we end up depending on a huge interface with lots of other single-use methods. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetOrdersById&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetOrdersByDate&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetLineItemsByOrderId&lt;/code&gt;…&lt;/p&gt;

&lt;h2 id=&quot;the-specification-pattern-simplifies-repositories&quot;&gt;The Specification Pattern Simplifies Repositories&lt;/h2&gt;

&lt;p&gt;With the Specification pattern, we extract the “query logic” to another object and away from our repositories.&lt;/p&gt;

&lt;p&gt;Instead of making our repositories more specific by adding more methods (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_repo.GetOrderById(123456)&lt;/code&gt;), the Specification pattern makes repositories more general (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_repo.FirstOrDefault(new OrderById(123456))&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Think of a Specification as the query logic and the query parameters to retrieve objects.&lt;/p&gt;

&lt;p&gt;Specifications make more sense when using &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;Domain-Driven Design&lt;/a&gt;. With Specifications, we encapsulate the LINQ queries, scattered all over our code, inside well-named objects that we keep inside our Domain layer.&lt;/p&gt;

&lt;p&gt;Here’s how to use the &lt;a href=&quot;https://github.com/ardalis/Specification&quot;&gt;Ardalis.Specification NuGet package&lt;/a&gt; to create a specification and retrieve a list of movies by their release year:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Ardalis.Specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Ardalis.Specification.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddScoped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddDbContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseInMemoryDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MovieDb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/movies/{releaseYear}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoviesByReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ListAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;byReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
        
    &lt;span class=&quot;c1&quot;&gt;// As an alternative, with Ardalis.Specification we can&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// use a specification directly with a DbContext:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//var movies = await aDbContext.Movies&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                .WithSpecification(byReleaseYear)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                .ToListAsync();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapPost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/movies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChangesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Or, simply with a DbContext:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//await aDbContext.Movies.AddAsync(movie);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//await anyDbContext.SaveChangesAsync();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;/movies/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoviesByReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoviesByReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RepositoryBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MoviesContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoviesContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MoviesContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ardalis.Specification provides a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RepositoryBase&amp;lt;T&amp;gt;&lt;/code&gt; class that wraps our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DbContext&lt;/code&gt; object and exposes the database operations using Specification objects. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListAsync()&lt;/code&gt; receives a specification, not an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IQueryable&lt;/code&gt; object, for example.&lt;/p&gt;

&lt;p&gt;Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repository&amp;lt;T&amp;gt;&lt;/code&gt; is simply a class definition without query logic. Just a couple of lines of code.&lt;/p&gt;

&lt;p&gt;Now the query logic is inside our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoviesByReleaseYear&lt;/code&gt;. Ardalis.Specification translates those filtering and ordering conditions to the right chain of Entity Framework Core methods.&lt;/p&gt;

&lt;p&gt;Our repositories are way simpler and the query logic is abstracted to another object.&lt;/p&gt;

&lt;h2 id=&quot;a-naive-implementation-of-the-specification-pattern&quot;&gt;A Naive Implementation of the Specification Pattern&lt;/h2&gt;

&lt;p&gt;The best way to understand a piece of code is to recreate a minimal version.&lt;/p&gt;

&lt;p&gt;Here’s a naive and bare-bones implementation of an in-memory repository that filters a list using a specification object:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Saving Private Ryan&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InMemoryRepo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ByReleaseYearSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// [ Movie { Name = Saving Private Ryan, ReleaseYear = 1998 } ]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ByReleaseYearSpec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ByReleaseYearSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InMemoryRepo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InMemoryRepo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_items&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                    ^^^^  &lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evaluator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SpecEvaluator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evaluator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ApplySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SpecEvaluator&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplySpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// The original implementation uses some &quot;evaluators&quot; to:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 1. Check if the spec has a certain shape and &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 2. Apply that shape to the input &quot;query&quot;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// For example, WhereEvaluator checks if the spec has a Where clause and&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// applies it.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// &lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This SpecEvaluator would be a Composite of smaller&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// evaluators that look for a certain shape&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Predicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All the magic is inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InMemoryRepo&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SpecEvaluator&lt;/code&gt; classes.&lt;/p&gt;

&lt;p&gt;In the original implementation, the &lt;a href=&quot;https://github.com/ardalis/Specification/blob/main/Specification.EntityFrameworkCore/src/Ardalis.Specification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs&quot;&gt;SpecEvaluator&lt;/a&gt; takes the parameters from the filters inside our specification (like Where, OrderBy, Skip, and Take) and apply them using EntityFramework Core methods into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IQueryable&lt;/code&gt; object that represents our database query.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to use the Specification pattern to make our repositories more SOLID. With the Specification pattern, our repositories have a slim interface and a single responsibility: to turn specifications into database calls.&lt;/p&gt;

&lt;p&gt;For more C# content, check &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;how to create test data with the Builder pattern&lt;/a&gt;, &lt;a href=&quot;/2021/02/10/DecoratorPattern/&quot;&gt;how to use the Decorator pattern with a real example&lt;/a&gt;, and &lt;a href=&quot;/2020/02/14/PipelinePattern/&quot;&gt;how to use the Pipeline pattern: An assembly line of steps&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Handle the Startup Class When Migrating ASP.NET Core Projects</title>
   <link href="https://canro91.github.io/2024/09/25/MigratingStartupClass/"/>
   <updated>2024-09-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/09/25/MigratingStartupClass</id>
   <content type="html">&lt;p&gt;.NET 6.0 replaced the Startup class with a new hosting model and a simplified Program.cs file.&lt;/p&gt;

&lt;p&gt;The Startup class is still available in newer versions. If we’re migrating a pre-.NET 6.0 project, the &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview&quot;&gt;.NET upgrade assistant tool&lt;/a&gt; does the work while keeping the Startup class.&lt;/p&gt;

&lt;p&gt;Here are 3 alternatives to handle with the Startup class when migrating to newer versions:&lt;/p&gt;

&lt;h2 id=&quot;1-official-docs-approach&quot;&gt;1. Official Docs Approach&lt;/h2&gt;

&lt;p&gt;Newer versions of ASP.NET Core work perfectly fine with the old Program.cs file and Startup class, we can choose to do nothing and keep them.&lt;/p&gt;

&lt;p&gt;Here’s an old-style Program class:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Hosting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Hosting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AnOldStyleAspNetCoreProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;CreateHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHostBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureWebHostDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                 &lt;span class=&quot;n&quot;&gt;webBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UseStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And a Startup class:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Hosting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.DependencyInjection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AnOldStyleAspNetCoreProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Startup&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseRouting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseAuthorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nothing fancy. A simple API project.&lt;/p&gt;

&lt;p&gt;If we want to keep the Startup class, here’s what &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/migration/50-to-60?view=aspnetcore-6.0&amp;amp;tabs=visual-studio#use-startup-with-the-new-minimal-hosting-model&quot;&gt;Microsoft official docs&lt;/a&gt; show to make the Startup class work with the new hosting model and the simplified Program.cs:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// vvvvv&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We created a new instance of Startup inside the new Program.cs file.&lt;/p&gt;

&lt;h2 id=&quot;2-hybrid-approach&quot;&gt;2. Hybrid Approach&lt;/h2&gt;

&lt;p&gt;If we really want to ditch the Startup class, Andrew Lock recommends &lt;a href=&quot;https://andrewlock.net/exploring-dotnet-6-part-12-upgrading-a-dotnet-5-startup-based-app-to-dotnet-6/#option-3-local-methods-in-program-cs&quot;&gt;a hybrid approach in his blog&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Turn the methods from the Startup class into private methods in the new Program.cs file.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^^&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This method used to be in Startup.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This method used to be in Startup.cs too&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseRouting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseAuthorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-do-it-yourself-approach&quot;&gt;3. Do-It-Yourself Approach&lt;/h2&gt;

&lt;p&gt;And if we want our Program.cs to look like the newer ones, there’s no automatic tool for that (at least I couldn’t find one in my Googling session). We have to copy the contents of the Startup class into the Program.cs file, by hand:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Put here what you had in ConfigureServices...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Put here what you had in Configure...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseAuthorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// app.UseRouting();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ASP0014: Suggest using top level route registrations instead of UseEndpoints&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//app.UseEndpoints(endpoints =&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//{&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//    endpoints.MapControllers();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//});&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// After:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We cut and pasted the content of Startup inside the right sections of the new Program.cs. This time, we get some warnings about deprecated methods and their alternatives.&lt;/p&gt;

&lt;p&gt;Voilà! If you’re a lazy developer like me, do nothing or go with the approach from the official docs. Otherwise, go with any of the other two options.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Four Lessons and a Challenge for a Coder Struggling to Write</title>
   <link href="https://canro91.github.io/2024/09/23/StrugglingToWrite/"/>
   <updated>2024-09-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/09/23/StrugglingToWrite</id>
   <content type="html">&lt;p&gt;Last week, Syed, one of my &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;email subscribers&lt;/a&gt;, shared his struggles with writing online.&lt;/p&gt;

&lt;p&gt;Here’s an edited version of Syed’s email:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I wanted to start writing about my debugging journey of the things I had been stuck with long time and then solving it finally… But I couldn’t continue as I thought my website wasn’t the best with SEO and no one may read it on my website. Then I shifted to Twitter and later, due to fewer engagements, I couldn’t continue there either. Well no I think maybe writing for some well-known forums might be the way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Syed and you that want to start writing as a software developer:&lt;/p&gt;

&lt;h2 id=&quot;dont-create-document&quot;&gt;Don’t Create, Document&lt;/h2&gt;

&lt;p&gt;Start by sharing what you do and what you learn. That’s a good start.&lt;/p&gt;

&lt;p&gt;Writing online is like keeping a public time capsule. If you don’t know what to add to your time capsule, &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;follow the 20-minute rule&lt;/a&gt;: if something takes you more than 20 minutes to figure out, write about it.&lt;/p&gt;

&lt;p&gt;Write about your learning and the problems you’re solving at work. Probably, the next time you’re googling something, you will find your own writing. That’s magical.&lt;/p&gt;

&lt;h2 id=&quot;if-you-want-to-start-writing-dont-start-your-own-blog&quot;&gt;If You Want to Start Writing, Don’t Start Your Own Blog&lt;/h2&gt;

&lt;p&gt;And don’t code your own blogging engine either.&lt;/p&gt;

&lt;p&gt;When we want to start writing, we fall back to doing what we know best — coding — and start by coding a blogging engine. That’s where writing and blogging die.&lt;/p&gt;

&lt;p&gt;The best place to start is on “social blogs.” Platforms for long-form content with readers and a distribution mechanism. This way, you don’t have to “chase” readers with SEO tricks and you’ll have faster feedback.&lt;/p&gt;

&lt;p&gt;If you don’t know where to start, go with &lt;a href=&quot;https://dev.to/canro91&quot;&gt;dev.to&lt;/a&gt; or &lt;a href=&quot;https://medium.com/@iamcesaraguirre&quot;&gt;Medium&lt;/a&gt;. I can’t recommend dev.to enough. It’s a beginner-friendly and welcoming platform for coders.&lt;/p&gt;

&lt;p&gt;Once you start on a social blog, keep your blog as your main hub or a portfolio of your favorite posts. That’s what I do.&lt;/p&gt;

&lt;h2 id=&quot;we-all-started-with-zero-readers-and-followers&quot;&gt;We All Started With Zero Readers and Followers&lt;/h2&gt;

&lt;p&gt;At the beginning, writing can feel lonely.&lt;/p&gt;

&lt;p&gt;I wrote my first online piece in 2018 and nobody read it. Maybe only one or two of my coworkers. I saw my blog analytics going from ~10 views per month to eventually ~1000s in a matter of years. Yes, you read that right. Years.&lt;/p&gt;

&lt;p&gt;Focus on writing your first 10 posts and keep trying and improving.&lt;/p&gt;

&lt;p&gt;Here’s when the &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt; attitude keeps us writing in the long run. And like any other infinite game, you only lose if you stop playing.&lt;/p&gt;

&lt;p&gt;Write for yourself and for sure others people will find it useful too.&lt;/p&gt;

&lt;h2 id=&quot;seo-is-another-skill-to-master&quot;&gt;SEO Is Another Skill to Master&lt;/h2&gt;

&lt;p&gt;If you go with a social blog, the platform does the SEO part for you. You don’t have to worry about it.&lt;/p&gt;

&lt;p&gt;Search engines keep changing their rules with algorithm updates. These days, it seems Google favors Reddit posts instead of personal blogs.&lt;/p&gt;

&lt;p&gt;You’ll be in a dead end if you try to chase every SEO update. Write for humans because search engines like it when you do that.&lt;/p&gt;

&lt;p&gt;Instead of trying SEO tricks, go with these rules:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Write to answer a query people might search for in a search engine.&lt;/li&gt;
  &lt;li&gt;Make your posts easy to read for humans: Don’t use big chunks of text. Instead, use subheaders to break those big chunks of text.&lt;/li&gt;
  &lt;li&gt;Link back to other posts using keywords: Don’t use “click here” or “see more.”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You see? Another skill to master. Go with a social blog.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought-and-challenge&quot;&gt;Parting Thought and Challenge&lt;/h2&gt;

&lt;p&gt;I owe my career growth to a couple of skills: apart from coding, learning English and writing online.&lt;/p&gt;

&lt;p&gt;After &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;blogging for more than five years&lt;/a&gt;, writing online has opened doors here and there. I made my first money on the internet thanks to my blog, for example.&lt;/p&gt;

&lt;p&gt;Even if you don’t make any money writing, it will give you clear thinking.&lt;/p&gt;

&lt;p&gt;And if you’ve made it this far, here’s my challenge: &lt;a href=&quot;https://dev.to/enter?state=new-user&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;create an account on dev.to&lt;/a&gt; and write your first 4 posts there and see where they take you. If you accept the challenge, contact me and share your first post.&lt;/p&gt;

&lt;p&gt;Write as if no one is reading and then keep writing because you don’t know who’s reading.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Four Writing Lessons I Learned After Binge-Reading Herbert Lui&apos;s Blog</title>
   <link href="https://canro91.github.io/2024/09/16/LessonsFromHerbertLuiBlog/"/>
   <updated>2024-09-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/09/16/LessonsFromHerbertLuiBlog</id>
   <content type="html">&lt;p&gt;A Sunday evening free of dopamine ended up being a lesson on writing and blogging.&lt;/p&gt;

&lt;p&gt;I started googling about note-taking and landed on Herbert Lui’s blog after finding &lt;a href=&quot;https://herbertlui.net/8-lessons-from-800-note-cards-in-the-zettelkasten/&quot;&gt;his lessons after 800 Zettelkasten notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In case you don’t know Herbert Lui’s work, he’s a writer, editorial director, and book author. He wrote &lt;a href=&quot;https://www.holloway.com/b/creative-doing&quot;&gt;Creative Doing&lt;/a&gt;, a book with exercises and prompts for writers and creatives.&lt;/p&gt;

&lt;p&gt;After binge-reading his blog, I learned these lessons from him.&lt;/p&gt;

&lt;h2 id=&quot;1-dont-start-from-scratch&quot;&gt;1. Don’t Start From Scratch&lt;/h2&gt;

&lt;p&gt;Be a DJ producer of ideas instead.&lt;/p&gt;

&lt;p&gt;Instead of trying to come up with original ideas every time, mix and build on your past and maybe forgotten ideas.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://herbertlui.net/dont-start-from-scratch/&quot;&gt;Don’t start from scratch again&lt;/a&gt;. Go back to one of your old ideas or writings and give it a fresh lick of paint.&lt;/p&gt;

&lt;h2 id=&quot;2-prefer-quantity-over-quality&quot;&gt;2. Prefer Quantity Over Quality&lt;/h2&gt;

&lt;p&gt;If you want to improve at something, go for quantity.&lt;/p&gt;

&lt;p&gt;That’s the key finding of the story of the pottery class: A teacher divided his pottery class into two halves. The first half was graded on the quantity of pottery produced and the other half on the quality instead. At the end of the class, the half graded on quantity did best. Their effort was to produce something and quickly move on to the next piece, taking the feedback from past repetitions.&lt;/p&gt;

&lt;p&gt;If you need ideas to go for quantity, do a 100-day challenge: finish something every day for 100 days. Herbert wrote for 100 days and &lt;a href=&quot;https://herbertlui.net/writing-every-day/&quot;&gt;shared his lessons&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quality comes after quantity.&lt;/p&gt;

&lt;h2 id=&quot;3-you-dont-run-out-of-ideas-if-you-know-where-to-look&quot;&gt;3. You Don’t Run Out of Ideas if You Know Where To Look&lt;/h2&gt;

&lt;p&gt;When you think you’re running out of ideas, go through your favorite blogs, YouTube channels, and your social feeds and write a reaction post for a piece you liked.&lt;/p&gt;

&lt;p&gt;That’s only one of the ideas I use to &lt;a href=&quot;/2024/09/09/WritingIdeas/&quot;&gt;never run of writing ideas&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;4-dont-write-masterpieces-plant-seeds-instead&quot;&gt;4. Don’t Write Masterpieces, Plant Seeds Instead&lt;/h2&gt;

&lt;p&gt;A post doesn’t have to be a 2,000-word masterpiece.&lt;/p&gt;

&lt;p&gt;To remove the pressure of writing masterpieces every time, consider a blog like a garden of ideas instead of a finished and polished product. You can always expand a past idea on a new piece.&lt;/p&gt;

&lt;p&gt;Most of Herbert’s posts only have a headline and one main idea. You’re free to break the rule of writing introductions and conclusions. That’s something I also noticed by reading &lt;a href=&quot;https://seths.blog/&quot;&gt;Seth Godin’s blog&lt;/a&gt;. They write the main idea naturally after the headline without an introduction.&lt;/p&gt;

&lt;p&gt;Finding Herbert’s blog inspired me to continue writing. Herbert doesn’t seem to have a “niche.” Maybe everything he writes falls under the umbrella of writing and creativity.&lt;/p&gt;

&lt;p&gt;Don’t have a niche and go for quantity. You don’t need to write masterpieces, only to document you journey.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Follow These 3 Tips To Never Run Out of Writing Ideas</title>
   <link href="https://canro91.github.io/2024/09/09/WritingIdeas/"/>
   <updated>2024-09-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/09/09/WritingIdeas</id>
   <content type="html">&lt;p&gt;Running out of ideas is the greatest fear of new writers.&lt;/p&gt;

&lt;p&gt;But don’t worry. You’re not running out of ideas. You have plenty of them. You need to be aware of the content you consume and have writing prompts to help you write.&lt;/p&gt;

&lt;p&gt;Here are 3 tips to never run out of ideas to write:&lt;/p&gt;

&lt;h2 id=&quot;1-become-a-dj-of-ideas&quot;&gt;1. Become a DJ of Ideas&lt;/h2&gt;

&lt;p&gt;There’s nothing new under the sun. And it has all been done before.&lt;/p&gt;

&lt;p&gt;That’s relieving and reassuring. That removes the self-imposed pressure of coming up with new and original ideas.&lt;/p&gt;

&lt;p&gt;Never start from scratch again. Develop your “new” ideas by remixing and expanding your past ideas or others’ ideas. Become a DJ producer of ideas.&lt;/p&gt;

&lt;p&gt;If you think you’re running out of ideas, go through your posts, videos, or social feed and write a reaction post about one piece of content.&lt;/p&gt;

&lt;h2 id=&quot;2-follow-the-20-min-rule&quot;&gt;2. Follow the 20-Min Rule&lt;/h2&gt;

&lt;p&gt;If something takes you 20 minutes or more to figure out, write about it.&lt;/p&gt;

&lt;p&gt;This is the prompt I’ve used to write some of my coding tutorials. Once I figure out how to do something after being stuck for a while, I write about it.&lt;/p&gt;

&lt;p&gt;I wish I could credit the source of this rule. I remember learning about it from an old YouTube presentation about technical blogging that I can’t find anymore.&lt;/p&gt;

&lt;p&gt;So if you have an aha moment, write about it. If you finally understood a hard subject, write about it.&lt;/p&gt;

&lt;p&gt;By writing about your learning struggles, you will reinforce and document your learning.&lt;/p&gt;

&lt;h2 id=&quot;3-follow-the-3-strike-rule&quot;&gt;3. Follow the 3-Strike Rule&lt;/h2&gt;

&lt;p&gt;If a subject or idea comes up at least three times around you, write about it.&lt;/p&gt;

&lt;p&gt;I found this idea on &lt;a href=&quot;https://www.swyx.io/three-strikes&quot;&gt;Shawn Wang (swyx)’s blog&lt;/a&gt; in the context of technical blogging. But it can be easily extended outside the coding world too.&lt;/p&gt;

&lt;p&gt;If another client asks you the same question, write about it. And if you give the same piece of advice to someone, guess what? Write about it.&lt;/p&gt;

&lt;p&gt;With the 3-strike rule, you’re also &lt;a href=&quot;/2021/12/06/ItsNotWhatYouRead/&quot;&gt;saving your keystrokes&lt;/a&gt;. After 3 strikes, the next time the same subject comes up, just share a link to your writing.&lt;/p&gt;

&lt;p&gt;Now, you’re not running out of ideas again. It’s impossible. Notice the content you’re consuming and write about it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What My First Job as a Software Engineer Taught Me About Coding and Life</title>
   <link href="https://canro91.github.io/2024/09/02/LessonsFromMyFirstCodingJob/"/>
   <updated>2024-09-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/09/02/LessonsFromMyFirstCodingJob</id>
   <content type="html">&lt;p&gt;My first coding job was far from being like a Silicon Valley job at a startup.&lt;/p&gt;

&lt;p&gt;I didn’t have ping-pong tables or slides to go between offices.&lt;/p&gt;

&lt;p&gt;It was, by all means, a boring job at a local non-tech company. There’s nothing wrong with a boring job if that’s what you want. But it taught me valuable lessons about life, coding, and money. Here they are.&lt;/p&gt;

&lt;h2 id=&quot;you-are-not-your-code&quot;&gt;You Are Not Your Code&lt;/h2&gt;

&lt;p&gt;My first job was with a small team. One team member took care of one project, from start to end, wearing multiple hats.&lt;/p&gt;

&lt;p&gt;Things got complicated when we had to work together and inherited the code from a coworker. It was messy code, to say the least. He put all actions and logic inside event handlers. Those were the days of WinForms applications: Drag and drop a button, double-click, and connect to the database right there.&lt;/p&gt;

&lt;p&gt;We had to fix his issues and rewrite his code. Nobody wanted to do that. And I started to judge him because of his code. “What if he does everything else the same way he codes?”&lt;/p&gt;

&lt;p&gt;Don’t judge someone by their code. Don’t take it personally either. You could miss professional connections or friendships by judging people for their code.&lt;/p&gt;

&lt;p&gt;Assume everyone does their best with the resources they have at hand.&lt;/p&gt;

&lt;p&gt;In the future, someone will inherit your code and say “What a crappy code. Who wrote this?”&lt;/p&gt;

&lt;p&gt;There will always be different opinions and better ways of doing things. And even you will think of better ways to do your current work!&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2024-09-02-LessonsFromMyFirstCodingJob/MyFirstJob.png&quot; alt=&quot;An OpenSearch dashboard with a red rectangle around it&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;That&apos;s not me at my first job. Source: stablediffusionai.ai&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;coding-is-not-the-only-thing&quot;&gt;Coding Is Not the Only Thing&lt;/h2&gt;

&lt;p&gt;In my first days, I only wanted to code.&lt;/p&gt;

&lt;p&gt;I had just finished reading the &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt; and wanted to refactor everything around me. I didn’t want to attend meetings, answer phone calls, or even reply to emails. I only wanted to code. That’s what I was paid for, right?&lt;/p&gt;

&lt;p&gt;More than once, my boss called me to his office and I arrived minutes late because I was coding in my cubicle. I don’t know why I didn’t get in trouble for that.&lt;/p&gt;

&lt;p&gt;Software engineering is about collaboration.&lt;/p&gt;

&lt;p&gt;You won’t be locked in a basement coding. You must talk to clients, conduct meetings, agree on estimations, and ask for help.&lt;/p&gt;

&lt;p&gt;I had to learn there’s more than just typing symbols in text files.&lt;/p&gt;

&lt;h2 id=&quot;live-with-half-of-your-salary&quot;&gt;Live With Half of Your Salary&lt;/h2&gt;

&lt;p&gt;That’s the best life advice I’ve ever received for free.&lt;/p&gt;

&lt;p&gt;My cubicle was next to the coffee machine, in a corner that had once been a bathroom.&lt;/p&gt;

&lt;p&gt;One day, another coworker, a “veteran of many battles,” came over to have his coffee. And he said something like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Hey Cesar, here’s a free piece of advice. Now that you can, imagine you only make half your salary and save the other half. Sooner than later, you can buy your own apartment.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Years later, while reading money books, I found similar advice. And it reminded me of that conversation.&lt;/p&gt;

&lt;p&gt;I’ve followed that advice, but not exactly. I’ve saved less than half of my salary.&lt;/p&gt;

&lt;p&gt;Today, I’d rephrase it like this: “Imagine you make half of your salary, save, and invest the other half.”&lt;/p&gt;

&lt;h2 id=&quot;you-dont-have-to-feel-miserable&quot;&gt;You Don’t Have To Feel Miserable&lt;/h2&gt;

&lt;p&gt;I thought talent and good work were shortcuts to breaking the rules of the corporate world. I was soooo wrong! The Matrix is real!&lt;/p&gt;

&lt;p&gt;Endless meetings, office politics, and a fixed schedule.&lt;/p&gt;

&lt;p&gt;It all started to take its toll on me.&lt;/p&gt;

&lt;p&gt;There were days when I felt I was leaving my life behind while sitting at a computer. I felt demotivated and disengaged. I was craving variety and change. I didn’t know there was a term for that: burnout.&lt;/p&gt;

&lt;p&gt;Always have an exit plan.&lt;/p&gt;

&lt;p&gt;Change jobs when you wake up and can’t get out of bed to work.&lt;/p&gt;

&lt;p&gt;Find a way to motivate yourself: start a side project, learn a new tech stack, or discover a new way of doing your work. Or simply update your CV and LinkedIn profile and move on.&lt;/p&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting Thoughts&lt;/h2&gt;

&lt;p&gt;It’s been more than 10 years since my first job. I’m only grateful for it. Somebody took a leap of faith with me and gave me a chance when I had 0 hours of flight time.&lt;/p&gt;

&lt;p&gt;I took these lessons to my next job. And every time I can, I give the same money advice my coworker gave me: save and invest half of your salary.&lt;/p&gt;

&lt;p&gt;Often what we value the most from past jobs is not the money, but the friendships and connections. From time to time, I meet with coworkers I met at this first job for coffee.&lt;/p&gt;

&lt;p&gt;For more content, read &lt;a href=&quot;/2024/06/24/LessonsForAMentee/&quot;&gt;8 lessons or so for new software developers&lt;/a&gt; and &lt;a href=&quot;/2024/04/29/2034Predictions/&quot;&gt;my predictions for Software Engineering in 2034&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>It Seems the C# Team Is Finally Considering Supporting Discriminated Unions</title>
   <link href="https://canro91.github.io/2024/08/19/DiscriminatedUnionSupport/"/>
   <updated>2024-08-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/08/19/DiscriminatedUnionSupport</id>
   <content type="html">&lt;p&gt;C# is getting more and more functional with every release.&lt;/p&gt;

&lt;p&gt;I don’t mean functional in the sense of being practical or not. I mean C# is borrowing features from functional languages, like records from F#, while staying a multi-paradigm language.&lt;/p&gt;

&lt;p&gt;Yes, C# will never be a fully functional language. And that’s by design.&lt;/p&gt;

&lt;p&gt;But, it still misses one key feature from functional languages: Discriminated Unions.&lt;/p&gt;

&lt;h2 id=&quot;discriminated-unions-are-a-closed-hierarchy-of-classes&quot;&gt;Discriminated Unions Are a Closed Hierarchy of Classes&lt;/h2&gt;

&lt;p&gt;Think of discriminated unions like enums where each member could be an object of a different, but somehow related, type.&lt;/p&gt;

&lt;p&gt;Let me show you an example where a discriminated union makes sense. At a past job, while working with a reservation management software, hotels wanted to charge a deposit before the guests arrived. They wanted to charge some nights, a fixed amount, or a percentage of room charges, right after getting the reservation or before the arrival date.&lt;/p&gt;

&lt;p&gt;Here’s how to represent that requirement with a discriminated union:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deposit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Of course, I&apos;m making this up...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// This is invalid syntax&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NightCount&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FixedAmount&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Percentage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;USD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Euro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MXN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;COP&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Days&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BeforeCheckin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AfterReservation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Charge&lt;/code&gt; class would be a discriminated union. It could only hold one of three values: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NightCount&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedAmount&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Percentage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You might be thinking it looks like a regular hierarchy of classes. And that’s right.&lt;/p&gt;

&lt;p&gt;But, the missing piece is that discriminated unions are exhaustive. We don’t need a default case when using a discriminated union inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt;. And, if we add a new member to the discriminated union, the compiler will warn us about where we should handle the new member.&lt;/p&gt;

&lt;p&gt;Discriminated unions are helpful to express restrictions, constraints, and business rules when working with Domain Driven Design. In fact, using types to represent business rules is the main idea of the book &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, discriminated unions are a good alternative when &lt;a href=&quot;/2024/08/05/IOToTheEdges/&quot;&gt;moving I/O to the edges of our apps&lt;/a&gt; and just returning decisions from our domains.&lt;/p&gt;

&lt;p&gt;I told you that C# is borrowing some new features from functional languages. Well, if you’re curious, this is how our example will look like in F# using real discriminated unions:&lt;/p&gt;

&lt;div class=&quot;language-fsharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deposit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;-- Look, ma! A discriminated union&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NightCount&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FixedAmount&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CurrencyCode&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Percentage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decimal&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CurrencyCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;USD&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Euro&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MXN&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;COP&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BeforeCheckin&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AfterReservation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All credits to &lt;a href=&quot;https://www.phind.com/search?home=true&quot;&gt;Phind&lt;/a&gt;, “an intelligent answer engine for developers,” for writing that F# example.&lt;/p&gt;

&lt;h2 id=&quot;a-proposal-for-a-union-keyword&quot;&gt;A Proposal for a Union Keyword&lt;/h2&gt;

&lt;p&gt;It seems the C# language team is considering fully supporting discriminated unions.&lt;/p&gt;

&lt;p&gt;In the official C# GitHub repository, there’s a recent &lt;a href=&quot;https://github.com/dotnet/csharplang/blob/18a527bcc1f0bdaf542d8b9a189c50068615b439/proposals/TypeUnions.md&quot;&gt;proposal for discriminated unions&lt;/a&gt;. The goal is to create a new type to “store one of a limited number of other types in the same place” and let C# do all the heavy work to handle variables of that new type, including checking for exhaustiveness.&lt;/p&gt;

&lt;p&gt;The proposal suggests introducing a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;union&lt;/code&gt; type for classes and structs. Our example using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;union&lt;/code&gt; type will look like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;FixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This is how to instantiate a union type&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chargeOneNight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oneNightBeforeCheckin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deposit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;chargeOneNight&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BeforeCheckin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This is how to use it inside a switch&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amountToCharge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;charge&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NightCount&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FixedAmount&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingElseHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Percentage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingDifferentHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// No need to declare a default case here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;union&lt;/code&gt; type will support pattern matching and deconstruction too.&lt;/p&gt;

&lt;p&gt;Under the hood, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;union&lt;/code&gt; type will get translated to a hierarchy of classes, with the base class annotated with a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Closed]&lt;/code&gt; attribute. And, if the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;union&lt;/code&gt; type doesn’t meet our needs, we can use that new attribute directly.&lt;/p&gt;

&lt;h2 id=&quot;two-alternatives-while-we-wait&quot;&gt;Two Alternatives While We Wait&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;union&lt;/code&gt; type is still under discussion. We’ll have to wait.&lt;/p&gt;

&lt;p&gt;In the meantime, we can emulate this behavior using third-party libraries like &lt;a href=&quot;https://github.com/mcintyre321/OneOf&quot;&gt;OneOf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how to define our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Charge&lt;/code&gt; type using OneOf:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OneOfBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                    ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Or using OneOf Source Generation:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//[GenerateOneOf] // &amp;lt;--&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//public partial class Charge : OneOfBase&amp;lt;NightCount, FixedAmount, Percentage&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                              ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//{&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;-- No base class here&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Here&apos;s how to instantiate a OneOf type&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oneNightBeforeCheckin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deposit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BeforeCheckin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;OneOf brings methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Match&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsT0&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsT1&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsT2&lt;/code&gt; to work with and unwrap the underlying type.&lt;/p&gt;

&lt;p&gt;Apart from third-party libraries to emulate discriminated unions, there’s an alternative approach abusing records: &lt;a href=&quot;https://github.com/salvois/DiscriminatedOnions&quot;&gt;Discriminated Onions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And here’s our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Charge&lt;/code&gt; type using discriminated onions:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;//    ^^^^^&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onNightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onFixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onPercentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;NightCount&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onNightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FixedAmount&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onFixedAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Percentage&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onPercentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentOutOfRangeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Here&apos;s how to instantiate a discriminated onion&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oneNightBeforeCheckin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deposit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NightCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BeforeCheckin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s what discriminated unions are, and the official proposal to bring them to the C# language.&lt;/p&gt;

&lt;p&gt;You see, C# is getting more functional on each release. While we wait to get it more funcy with discriminated unions, we have to go with libraries and workarounds. Let’s wait to see how it goes.&lt;/p&gt;

&lt;p&gt;For more content, read &lt;a href=&quot;/2024/07/08/CSharpInconsistencies/&quot;&gt;What I Don’t Like About C# Evolution&lt;/a&gt;, &lt;a href=&quot;/2023/08/07/TooManyLayers/&quot;&gt;An Alternative to Simplify Layering For Read-only Data Access&lt;/a&gt; and, &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;How I Choose Value Objects&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>For Cleaner Domains, Move IO to the Edges of Your App</title>
   <link href="https://canro91.github.io/2024/08/05/IOToTheEdges/"/>
   <updated>2024-08-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/08/05/IOToTheEdges</id>
   <content type="html">&lt;p&gt;Don’t get too close with I/O.&lt;/p&gt;

&lt;p&gt;That’s how I’d summarize the talk “Moving IO to the edges of your app” by Scott Wlaschin at NDC Sydney 2024.&lt;/p&gt;

&lt;p&gt;In case you don’t know Scott Wlaschin’s work, he runs the site &lt;a href=&quot;https://fsharpforfunandprofit.com/&quot;&gt;F# for Fun and Profit&lt;/a&gt; and talks about Functional Programming a lot. He’s a frequent speaker at the NDC Conference.&lt;/p&gt;

&lt;p&gt;Here’s the YouTube video of the talk, in case you want to watch it:&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/P1vES9AgfC4?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;These are the main takeaways from that talk and how I’d follow them to refactor a piece of code from one of my past projects.&lt;/p&gt;

&lt;h2 id=&quot;io-is-evil-keep-it-at-arms-length&quot;&gt;I/O Is Evil: Keep It at Arm’s Length&lt;/h2&gt;

&lt;p&gt;In a perfect world, all code should be pure. The same inputs return the same outputs with no side effects.&lt;/p&gt;

&lt;p&gt;But we’re not in a perfect world, and our code is full of impurities: retrieving the current time, accessing the network, and calling databases.&lt;/p&gt;

&lt;p&gt;Instead of aiming for 100% pure code, the guideline is to &lt;strong&gt;move I/O (or impurities) away from the business logic or rules&lt;/strong&gt;.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2024-08-05-IOToTheEdges/IoAtTheEdges.png&quot; alt=&quot;IO at the edges&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Move IO to the Edges. Created based on speaker&apos;s slides&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;When we mix I/O with our domain logic, we make our domain logic harder to understand and test, and more error-prone.&lt;/p&gt;

&lt;p&gt;So let’s pay attention to functions with no inputs or no outputs. Often, they do I/O somewhere.&lt;/p&gt;

&lt;p&gt;If you think we don’t write functions with no outputs, let’s take another look at our repositories.&lt;/p&gt;

&lt;p&gt;Sure, our Create or Update methods might return an ID. But they’re not deterministic. If we insert the same record twice, we get different IDs or even an error if we have unique constraints in our tables.&lt;/p&gt;

&lt;p&gt;The guideline here is to write code that is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Comprehensible&lt;/strong&gt;: it receives what it needs as input and returns some output.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deterministic&lt;/strong&gt;: it returns the same outputs, given the same input.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Free of side effects&lt;/strong&gt;: it doesn’t do anything under the hood.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;just-return-the-decision&quot;&gt;Just Return the Decision&lt;/h2&gt;

&lt;p&gt;This is the example shown in the talk:&lt;/p&gt;

&lt;p&gt;Let’s say we need to update a customer’s personal information. If the customer changes their email, we should send a verification email. And, of course, we should update the new name and email in the database.&lt;/p&gt;

&lt;p&gt;This is how we might do that,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CustomerDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CustomerDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EmailMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Some message here...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EmailServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SendMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re mixing the database calls with our decision-making code. IO is “close” to our business logic.&lt;/p&gt;

&lt;p&gt;Of course, we might argue static methods are a bad idea and pass two interfaces instead: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICustomerDb&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEmailServer&lt;/code&gt;. But we’re still mixing IO with business logic.&lt;/p&gt;

&lt;p&gt;This time, the guideline is to &lt;strong&gt;create an imperative shell and just return the decision from our business logic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how to update our customers “just returning the decision,”&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateCustomerDecision&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DoNothing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UpdateCustomerOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UpdateCustomerAndSendEmail&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This is a good place for discriminated unions.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// But we still don&apos;t have them in C#. Sorry!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateCustomerResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UpdateCustomerDecision&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Decision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;EmailMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateCustomerResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateCustomerResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateCustomerDecision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoNothing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Decision&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateCustomerDecision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateCustomerOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EmailMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Some message here...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Decision&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateCustomerDecision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateCustomerAndSendEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ImperativeShell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CustomerDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Nothing impure here&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoNothing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Well, doing nothing...&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateCustomerOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Updating the database here...&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateCustomerAndSendEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Update the database here...&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// And, send the email here...&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the imperative shell, we don’t have to deal with database calls and email logic inside our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateCustomer()&lt;/code&gt;. And we can unit test it without mocks.&lt;/p&gt;

&lt;p&gt;As a side note, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateCustomerDecision&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateCustomerResult&lt;/code&gt; are a simple alternative to &lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;discriminated unions&lt;/a&gt;. Think of discriminated unions like enums where each member could be an object of a different type.&lt;/p&gt;

&lt;p&gt;In more complex codebases, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImperativeShell()&lt;/code&gt; would be like a use case class or command handler.&lt;/p&gt;

&lt;h2 id=&quot;pure-code-doesnt-talk-to-the-outside&quot;&gt;Pure Code Doesn’t Talk to the Outside&lt;/h2&gt;

&lt;p&gt;When we push I/O to the edges, our pure code doesn’t need exception handling or asynchronous logic. Our pure code doesn’t talk to the outside world.&lt;/p&gt;

&lt;p&gt;These are the three code smells the speaker shared to watch out for in our domain code:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Is it async?&lt;/strong&gt; If so, you’re doing I/O somewhere&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Is it catching exceptions?&lt;/strong&gt; Again, you’re (probably) doing I/O somewhere&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Is it throwing exceptions?&lt;/strong&gt; Why not use a proper return value?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any of these are true, we’re doing IO inside our domain. And we should refactor our code. “All hands man your refactoring stations.”&lt;/p&gt;

&lt;h2 id=&quot;moving-io-to-the-edges-when-sending-an-email&quot;&gt;Moving I/O to the Edges When Sending an Email&lt;/h2&gt;

&lt;p&gt;While watching this talk, I realized I could refactor some code I wrote for sending emails in a past project.&lt;/p&gt;

&lt;p&gt;Before sending an email, we need to validate if we’re sending it to valid domains. And, after calling a third-party email service, we should store a tracking number and update the email status. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Email&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Imagine more properties like From, Subject, Body here...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_recipients&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SendAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IEmailService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IDomainValidationService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValidateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// It assumes that ValidateAsync changes the recipient&apos;s status&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastStatus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FailedOnSend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trackingId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SendEmailAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;SetTrackingId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trackingId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;MarkAsSentToProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;UpdateStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FailedOnSend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SendEmailException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sending email failed.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But this code contains the three code smells we should avoid: it has asynchronous logic and throws and catches exceptions, and even our Domain is aware of cancellation tokens. Arrggg!&lt;/p&gt;

&lt;p&gt;That was an attempt to do Domain Driven Design (DDD) at a past team. And probably, our team at that time picked those conventions from the book &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;Hands-on Domain-Driven Design with .NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And the imperative shell that calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendAsync()&lt;/code&gt; is something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SendEmailHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailCreatedEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Imagine some fields and a constructor here...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailCreatedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EmailNotFoundException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SendAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_emailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_validationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetFailedOnSend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s the same logic “returning the decision,”&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This is a poor man&apos;s discriminated union&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SentToSome&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Guid&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrackingId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SentToNone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FailedToSend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Email&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Imagine more properties like From, Subject, Body here...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_recipients&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SentToSome&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Set trackingId and mark as Sent for some recipients&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Mark all other recipients as Invalid&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SentToNone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// Mark all recipients as Invalid&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FailedToSend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Mark all recipients as Failed&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this refactored version, we’ve removed the asynchronous logic and exception handling. Now, it receives a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendingAttempt&lt;/code&gt; with the result of validating domains and email delivery to the email provider.&lt;/p&gt;

&lt;p&gt;Also, it doesn’t have any dependencies passed as interfaces. It embraces Dependency Rejection.&lt;/p&gt;

&lt;p&gt;And here’s the imperative shell,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SendEmailHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailCreatedEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Imagine some fields and a constructor here...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailCreatedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EmailNotFoundException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_validationService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValidateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// Use result to find valid and invalid destinations...&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Attempt to send email and catch any exceptions...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sendingAttempt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildASendingAttemptHere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendingAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;//    ^^^^&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// Nothing impure here&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, the imperative shell validates email domains and tries to send the email, encapsulating all the I/O around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Send()&lt;/code&gt;. After this refactoring, we should rename &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Send()&lt;/code&gt; inside our domain to something else.&lt;/p&gt;

&lt;p&gt;Voila! That’s one approach to have pure business logic, not the one and only approach.&lt;/p&gt;

&lt;p&gt;Whether we follow Ports and Adapters, Clean Architecture, or Functional Core-Imperative Shell, the goal is to abstract dependencies and avoid “contaminating” our business domain.&lt;/p&gt;

&lt;p&gt;For more content on architecture and modeling, check &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional: Takeaways&lt;/a&gt; and 
&lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;To Value Object or Not To: How I choose Value Objects&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>5 Unit Testing Best Practices I Learned from This NDC Conference Talk</title>
   <link href="https://canro91.github.io/2024/07/22/TestingTipsFromAnNDCTalk/"/>
   <updated>2024-07-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/07/22/TestingTipsFromAnNDCTalk</id>
   <content type="html">&lt;p&gt;Recently, I found a NDC talk titled “.NET Testing Best Practices” by Rob Richardson.&lt;/p&gt;

&lt;p&gt;Today I want to share five unit testing best practices I learned from that talk, along with my comments on other parts of it.&lt;/p&gt;

&lt;p&gt;Here’s the YouTube video of the talk, in case you want to watch it, and &lt;a href=&quot;https://robrich.org/&quot;&gt;the speaker’s website&lt;/a&gt;,&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/_USCe5PolOA?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;During the presentation, the speaker coded some unit tests for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightActuator&lt;/code&gt; class. This class powers an IoT device that turns a light switch on or off based on a motion sensor input.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightActuator&lt;/code&gt; turns on lights if any motion is detected in the evening or at night. And, it turns off lights in the morning or if no motion has been detected in the last minute.&lt;/p&gt;

&lt;p&gt;Here’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightActuator&lt;/code&gt; class, &lt;a href=&quot;https://github.com/robrich/net-testing-xunit-moq/blob/main/start/LightController/ApplicationCode.cs#L42&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILightActuator&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Update the time of last motion.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// If motion was detected in the evening or at night, turn the light on.&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTimePeriod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Evening&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Night&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;LightSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TurnOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// If no motion is detected for one minute, or if it is morning or day, turn the light off.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Subtract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromMinutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Morning&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Noon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;LightSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TurnOff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTimePeriod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hour&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hour&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Night&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hour&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hour&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Morning&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hour&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Hour&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Afternoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Evening&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s the first unit test the presenter live-coded,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LightActuator_ActuateLights_Tests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Arrange&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// random value&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Act&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Assert&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, the presenter refactored this test and introduced more examples throughout the rest of the talk. But this initial test is enough to prove our points.&lt;/p&gt;

&lt;h2 id=&quot;five-unit-testing-best-practices-from-this-talk&quot;&gt;Five Unit Testing Best Practices from This Talk&lt;/h2&gt;

&lt;h3 id=&quot;1-adopt-a-new-naming-convention&quot;&gt;1. Adopt a new naming convention&lt;/h3&gt;

&lt;p&gt;In this talk, I found a new &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;naming convention for our unit tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To name test classes, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;ClassName&amp;gt;_&amp;lt;MethodName&amp;gt;_Tests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For test methods, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Scenario&amp;gt;_&amp;lt;ExpectedResult&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here are the test class and method names for our sample test,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LightActuator_ActuateLights_Tests&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//          ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Magic goes here&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-label-your-test-parameters&quot;&gt;2. Label your test parameters&lt;/h3&gt;

&lt;p&gt;Instead of simply calling the method under test with a list of parameters, let’s label them for more clarity.&lt;/p&gt;

&lt;p&gt;For example, instead of simply calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActuateLights()&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, let’s create a documenting variable,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Before&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//actuator.ActuateLights(true);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                       ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// After&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-use-human-friendly-assertions&quot;&gt;3. Use human-friendly assertions&lt;/h3&gt;

&lt;p&gt;Looking closely at the sample test, we notice the Assert part has a bug.&lt;/p&gt;

&lt;p&gt;The actual and expected values inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NotEqual()&lt;/code&gt; are in the wrong order. The expected value should go first. Arrrggg!&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// They&apos;re in the wrong order. Arrrggg!&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To avoid flipping them again, it’s a good idea to use more human-friendly assertions using libraries like &lt;a href=&quot;https://github.com/fluentassertions/fluentassertions&quot;&gt;FluentAssertions&lt;/a&gt; or &lt;a href=&quot;https://github.com/shouldly/shouldly&quot;&gt;Shouldly&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s our tests using FluentAssertions,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Before&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//Assert.NotEqual(actualTime, startTime);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// They&apos;re in the wrong order. Arrrggg!&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// After, with FluentAssertions&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotBe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-dont-be-too-dry&quot;&gt;4. Don’t be too DRY&lt;/h3&gt;

&lt;p&gt;Our sample test only covers the scenario when any motion is detected. If we write another test for the scenario with no motion detected, our tests look like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LightActuator_ActuateLights_Tests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                    ^^^^&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NoMotionDetected_LastMotionTimeIsNotChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                    ^^^^^&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only difference between the two is the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;motionDetected&lt;/code&gt; and the assertion method at the end.&lt;/p&gt;

&lt;p&gt;We might be tempted to remove that duplication, using &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;parameterized tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But, &lt;strong&gt;inside unit tests, being explicit is better than being DRY.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Turning our two tests into a parameterized test would make us write a weird Assert part to switch between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equal()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NotEqual()&lt;/code&gt; based on the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;motionDetected&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s prefer clarity over dryness. Tests serve as a living documentation of system behavior.&lt;/p&gt;

&lt;h3 id=&quot;5-replace-dependency-creation-with-auto-mocking&quot;&gt;5. Replace dependency creation with auto-mocking&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActuateLights()&lt;/code&gt; uses a static class to turn on/off lights,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTimePeriod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Evening&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Night&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;LightSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TurnOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Subtract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromMinutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Morning&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timePeriod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Noon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;LightSwitcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TurnOff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’d be hard to assert if the lights were turned on or off with a static method.&lt;/p&gt;

&lt;p&gt;A better approach is to replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightSwitcher.Instance&lt;/code&gt; with an interface.&lt;/p&gt;

&lt;p&gt;But adding a new dependency to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightActuator&lt;/code&gt; would break our tests.&lt;/p&gt;

&lt;p&gt;Instead of manually passing the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightSwitch&lt;/code&gt; abstraction to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightActuator&lt;/code&gt; constructor inside our tests, we could rely on auto-mocking tools like &lt;a href=&quot;https://github.com/moq/Moq.AutoMocker&quot;&gt;Moq.AutoMocker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s our test using AutoMocker,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ioc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AutoMocker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ioc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                 ^^^^^&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotBe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve already used a similar approach with &lt;a href=&quot;/2021/06/21/WriteSimplerTestsTypeBuilderAndAutoFixture/&quot;&gt;TypeBuilder and AutoFixture&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;my-reaction-what-id-do-differently&quot;&gt;My Reaction: What I’d do differently&lt;/h2&gt;

&lt;p&gt;After writing and getting my tests reviewed, I’ve developed my own “taste” for unit testing.&lt;/p&gt;

&lt;p&gt;Don’t take me wrong. This is a good talk and I’ve stolen some ideas for my own presentations.&lt;/p&gt;

&lt;p&gt;But, this is what I’d do differently:&lt;/p&gt;

&lt;h3 id=&quot;1-avoid-comments-for-aaa-sections&quot;&gt;1. Avoid comments for AAA sections&lt;/h3&gt;

&lt;p&gt;Let’s avoid adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// Arrange&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// Act&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// Assert&lt;/code&gt; comments inside our tests.&lt;/p&gt;

&lt;p&gt;We don’t add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// class&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// fields&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// methods&lt;/code&gt; in other parts of our code, so it shouldn’t be necessary in our tests either.&lt;/p&gt;

&lt;p&gt;Instead, I prefer using blank lines to visually separate the three sections of the &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;Arrange/Act/Assert pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the examples I’ve shown you, I completely removed those comments.&lt;/p&gt;

&lt;h3 id=&quot;2-name-test-values-instead-of-using-comments&quot;&gt;2. Name test values instead of using comments&lt;/h3&gt;

&lt;p&gt;It’s a good idea to document our test values. But, let’s avoid using comments when we can use a descriptive name.&lt;/p&gt;

&lt;p&gt;I’d rename &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startTime&lt;/code&gt; with a comment at the end to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anyStartTime&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;randomStartTime&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MotionDetected_LastMotionTimeChanged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//DateTime startTime = new DateTime(2000, 1, 1); // random value&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                                               ^^^^^&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anyStartTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//var randomStartTime = new DateTime(2000, 1, 1);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//    ^^^^&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;LightActuator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightActuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anyStartTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ActuateLights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;motionDetected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actuator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMotionTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anyStartTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-dont-expose-private-parts&quot;&gt;3. Don’t expose private parts&lt;/h3&gt;

&lt;p&gt;In the talk, as part of the refactoring session, the presenter tested some internals. Specifically, he made the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LastMotionTime&lt;/code&gt; property inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LightActuator&lt;/code&gt; class public to use it inside the tests.&lt;/p&gt;

&lt;p&gt;Even somebody in the audience raised this question too.&lt;/p&gt;

&lt;p&gt;I understand the presenter had less than an hour to show a complete example and he chose a simple approach.&lt;/p&gt;

&lt;p&gt;But, let’s avoid exposing internals to our tests. &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;That’s the most common mistake on unit testing&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting thoughts&lt;/h2&gt;

&lt;p&gt;Voilà! Those are the five lessons I learned from this talk.&lt;/p&gt;

&lt;p&gt;My favorite quote from the talk:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“What’s cool about unit testing is we can debug our code by writing code”&lt;/p&gt;

  &lt;p&gt;— Rob Richardson&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As an exercise left to the reader, the presenter didn’t cover testing time. But we already covered &lt;a href=&quot;/2021/05/10/WriteTestsThatUseDateTimeNow/&quot;&gt;how to write tests that use DateTime.Now&lt;/a&gt; using a custom abstraction.&lt;/p&gt;

&lt;p&gt;And if we’re using .NET 8.0, we can rely on &lt;a href=&quot;/2024/06/10/TestingTimeWithTimeProvider/&quot;&gt;.NET 8.0 TimeProvider and its abstractions&lt;/a&gt; to abstract and test time.&lt;/p&gt;

&lt;p&gt;Another thing I didn’t like is that at some point in the testing session, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimePeriodHelper&lt;/code&gt; was added. And that’s one of the &lt;a href=&quot;/2022/12/07/BanningSomeNamingConventions/&quot;&gt;method and class names I’d like to ban&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>What I Don&apos;t Like About C# Evolution: Inconsistency</title>
   <link href="https://canro91.github.io/2024/07/08/CSharpInconsistencies/"/>
   <updated>2024-07-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/07/08/CSharpInconsistencies</id>
   <content type="html">&lt;p&gt;C# isn’t just Java anymore.&lt;/p&gt;

&lt;p&gt;That might have been true for the early days of C#. But the two languages took different paths.&lt;/p&gt;

&lt;p&gt;People making that joke have missed at least the last ten years of C# history.&lt;/p&gt;

&lt;p&gt;C# is open source and in constant evolution. In fact, you can upvote and discuss feature proposals in the &lt;a href=&quot;https://github.com/dotnet/csharplang&quot;&gt;C# official GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every .NET release comes with new C# features.&lt;/p&gt;

&lt;p&gt;But I’ve stopped obsessing over new C# features. In fact, I stopped collecting &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;my favorite C# features by version&lt;/a&gt;. I feel I’m not missing much.&lt;/p&gt;

&lt;p&gt;C# is not a consistent language anymore.&lt;/p&gt;

&lt;p&gt;We now have many alternatives for the same task. Don’t believe me?&lt;/p&gt;

&lt;h2 id=&quot;objects-arrays-and-null&quot;&gt;Objects, Arrays, and Null&lt;/h2&gt;

&lt;p&gt;First, here’s how to create a new object,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//        ^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last one is the target-typed “new” expressions introduced in C# 9.0. That’s one of the features I disable in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.editorconfig&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Second, here’s how to declare and initialize an array,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                         ^^^^^&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//              ^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We combine “new” expressions and collection expressions introduced in C# 12.&lt;/p&gt;

&lt;p&gt;And, lastly, here’s &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;how to check if an object is not null&lt;/a&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;titanic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;titanic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;titanic&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;titanic&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;titanic&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And I don’t want to touch on primary constructors. It’s like classes got jealous of records and started crying for a similar feature, like a baby boy jealous of his brother.&lt;/p&gt;

&lt;p&gt;Voilà! That’s what I don’t like about C#. Don’t take me wrong, C# is a great language with excellent tooling. But my favorite features are quite old: &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;LINQ&lt;/a&gt;, async/await, and extension methods.&lt;/p&gt;

&lt;p&gt;Some new features have lowered the barrier to entry. Now a “Hello, world” is a single line of code: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Console.WriteLine(&quot;Hello, world!&quot;);&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Other C# features are making the language inconsistent and easier to write but not easier to read.&lt;/p&gt;

&lt;p&gt;Often C# doesn’t feel like a single language, but three: the classic one from the early 2000s, another one between the classic and the .NET era, and the one we’re living these days.&lt;/p&gt;

&lt;p&gt;Anyway, &lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;still no discriminated unions&lt;/a&gt;. Maybe in C# 20?&lt;/p&gt;

&lt;p&gt;For more C# content, read &lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;my C# Definitive Guide&lt;/a&gt;, &lt;a href=&quot;/2020/08/01/AnotherTwoCSharpIdiomsPart3/&quot;&gt;how to avoid exceptions when working with Dictionaries&lt;/a&gt;, and &lt;a href=&quot;/2022/12/16/HelperMethodsOnCollections/&quot;&gt;Six helpful extension methods to work with Collections&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>New Developers Looking for a Mentor: Here&apos;s a (Free) Mentorship Session in 8 Lessons</title>
   <link href="https://canro91.github.io/2024/06/24/LessonsForAMentee/"/>
   <updated>2024-06-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/06/24/LessonsForAMentee</id>
   <content type="html">&lt;p&gt;You don’t need to meet your mentors.&lt;/p&gt;

&lt;p&gt;I read that Napoleon Hill, the author of “Think and Grow Rich,” talked to his mentor only once, and he said it changed his life.&lt;/p&gt;

&lt;p&gt;I’d be happy to share some advice over a virtual coffee in an “ask-a-friend” style. But to &lt;a href=&quot;/2021/12/06/ItsNotWhatYouRead/&quot;&gt;preserve my keystrokes&lt;/a&gt; and help more than one person, here I go.&lt;/p&gt;

&lt;p&gt;I’m writing for my 20-year-old self joining his first corporate job, pretending that talent was a shortcut to break the rules of the corporate world. I thought my first job would be like in a Silicon Valley startup, working barefoot and sliding between offices. It was far from that.&lt;/p&gt;

&lt;p&gt;This is free Internet advice, so handle it with care.&lt;/p&gt;

&lt;p&gt;If you could take away only one lesson, take this first one,&lt;/p&gt;

&lt;h2 id=&quot;1-learn-it-and-teach-it&quot;&gt;1. Learn It and Teach It&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The moment you learn something, teach it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s from the book &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt; by Austin Kleon. &lt;em&gt;“A book for people who hate the very idea of self-promotion.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Teaching is the most effective way to learn. When you teach what you’ve learned, you consolidate your learning and, if you choose to teach online, you start building an online brand.&lt;/p&gt;

&lt;p&gt;If you’re still here for more lessons, let’s continue…&lt;/p&gt;

&lt;h2 id=&quot;2-google-how-to-be-a-good-developer&quot;&gt;2. Google “How To Be a Good Developer”&lt;/h2&gt;

&lt;p&gt;I remember doing this exact same search, back in the early 2010s when I started my journey.&lt;/p&gt;

&lt;p&gt;I remember finding &lt;em&gt;“write technical specs”&lt;/em&gt; and &lt;em&gt;“learn functional programming.”&lt;/em&gt; It gave me ideas and subjects to explore. That’s why I’ve always had Functional Programming in the back of my head.&lt;/p&gt;

&lt;p&gt;I’d tell my younger self to do it again.&lt;/p&gt;

&lt;p&gt;Google or DuckDuckGo or Bing &lt;em&gt;“How to be a good developer”&lt;/em&gt; and see how deep the rabbit hole goes.&lt;/p&gt;

&lt;p&gt;Be careful, you will find one post saying &lt;em&gt;“Document your code”&lt;/em&gt; and another one, saying &lt;em&gt;“Don’t document your code.”&lt;/em&gt; Separate the wheat from the shaft.&lt;/p&gt;

&lt;h2 id=&quot;3-practice-a-lot&quot;&gt;3. Practice a Lot&lt;/h2&gt;

&lt;p&gt;The barrier to entry into the coding world is low, and lower and lower with every day that passes.&lt;/p&gt;

&lt;p&gt;Anyone can potentially start, but getting good at it takes years.&lt;/p&gt;

&lt;p&gt;Another googling task: search for “Teach Yourself Programming in Ten Years.”&lt;/p&gt;

&lt;p&gt;Start by writing an end-to-end project: a coding project that reads data from a webpage, calls a backend, persists data into a relational database, and displays it back.&lt;/p&gt;

&lt;p&gt;You will learn a lot from this simple exercise. HTML/CSS, a UI library, HTTP/REST, a backend language, SQL, and a database engine. Quite a lot!&lt;/p&gt;

&lt;p&gt;I can’t remember how many hours I spent coding a recipe catalog back in the day with PHP and MySQL.&lt;/p&gt;

&lt;h2 id=&quot;4-master-the-fundamentals&quot;&gt;4. Master the Fundamentals&lt;/h2&gt;

&lt;p&gt;Frameworks and libraries come and go.&lt;/p&gt;

&lt;p&gt;Study subjects that stand the test of time. Design patterns, data structures, and SQL, for example.&lt;/p&gt;

&lt;p&gt;For some reason, we’re still talking about stoicism thousands of years later.&lt;/p&gt;

&lt;p&gt;Probably in the next decade, we will still be writing code on text files, using some flavor of Unix/Linux, and writing SQL. I wouldn’t bet all my money, though.&lt;/p&gt;

&lt;h2 id=&quot;5-invest-in-your-soft-skills&quot;&gt;5. Invest in Your Soft Skills&lt;/h2&gt;

&lt;p&gt;You won’t be locked in your basement coding. This is a collaborative endeavor.&lt;/p&gt;

&lt;p&gt;You will spend most of your time communicating and collaborating: 1-1s, estimations, and SCRUM “ceremonies.”&lt;/p&gt;

&lt;p&gt;Invest in your soft skills. Start by reading “How to Win Friends and Influence People.” My biggest takeaway from that book is never telling someone is wrong.&lt;/p&gt;

&lt;h2 id=&quot;6-read-the-clean-code&quot;&gt;6. Read the Clean Code&lt;/h2&gt;

&lt;p&gt;This is the book we all start with.&lt;/p&gt;

&lt;p&gt;It opened a whole new world for me. Nobody taught me about good variable and function names until I found that book. I even remember one of my teachers using “stu1” and “stu2” as variable names during classes. Uncle Bob would be pissed by those names.&lt;/p&gt;

&lt;p&gt;Don’t just read it. Study it.&lt;/p&gt;

&lt;p&gt;But don’t become a &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt; police officer issuing infractions around you and using your book copy as your baton. There’s always a tradeoff.&lt;/p&gt;

&lt;h2 id=&quot;7-ask-yourself-what-you-want&quot;&gt;7. Ask Yourself What You Want&lt;/h2&gt;

&lt;p&gt;Start by experimenting with roles and tech stacks. You will find the one that you like the most.&lt;/p&gt;

&lt;p&gt;I started writing PDF reports by hand, drawing lines and cells, one pixel and line at a time. I don’t do that anymore, by the way. Then, I did a bit of mobile development with Xamarin and frontend development back in the day of Bootstrap and Knockout.js. Eventually, I got tired of styling and coloring issues and moved to backend development.&lt;/p&gt;

&lt;p&gt;Ask yourself what you want out of your career. Is it money? Connections? Growing your own business? Write a 5-year plan and be willing to correct the course. “No plan resists contact with the enemy” or reality, I prefer to say.&lt;/p&gt;

&lt;h2 id=&quot;8-climbing-the-corporate-ladder-is-a-myth&quot;&gt;8. Climbing the Corporate Ladder Is a Myth&lt;/h2&gt;

&lt;p&gt;You don’t control anything about the ladder you climb. Anyone can add a new step or replace the entire ladder, at any time.&lt;/p&gt;

&lt;p&gt;Software Engineer I, II, III, IV, V…&lt;/p&gt;

&lt;p&gt;You will hit a point of diminishing returns and a glass ceiling as a software developer.&lt;/p&gt;

&lt;p&gt;I’d tell my younger self eager for a fancy title: a new title comes with more meetings, extra hours, and the same salary.&lt;/p&gt;

&lt;p&gt;Instead of optimizing for a title, optimize for a lifestyle. The best ladder to climb is the one you build for yourself.&lt;/p&gt;

&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;

&lt;p&gt;Voila! That’s what I’d tell my younger self. I hope I haven’t discouraged you. Software Engineering is a great career. It’s the future. Well, that’s what I’ve been hearing for quite a while.&lt;/p&gt;

&lt;p&gt;The most satisfying thing about this career is seeing a smile on your users’ faces when what you code helps them save hours of repetitive work.&lt;/p&gt;

&lt;p&gt;For more practical advice…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you’re taking the self-studying route, I wrote to answer a reader asking &lt;a href=&quot;/2023/07/24/AdviceToStartAnUltralearningProject/&quot;&gt;how to start an ultralearning project to become a software engineer&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;If you’re studying C#, I wrote a guide with what I believe &lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;every C# developer should know&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Also, I challenged myself to learn enough &lt;a href=&quot;/2020/10/26/ReactIn30Days/&quot;&gt;React in 30 days&lt;/a&gt;. Probably by the time you read it, there will be a new React version doing everything differently. And, if you’re studying Golang, I studied enough &lt;a href=&quot;/2020/07/05/LetsGoStudyPlan/&quot;&gt;Go in 30 days&lt;/a&gt; too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See you around and happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Testing DateTime.Now Revisited: .NET 8.0 TimeProvider</title>
   <link href="https://canro91.github.io/2024/06/10/TestingTimeWithTimeProvider/"/>
   <updated>2024-06-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/06/10/TestingTimeWithTimeProvider</id>
   <content type="html">&lt;p&gt;Starting from .NET 8.0, we have new abstractions for time. We don’t need a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt; interface. There’s one built-in. Let’s learn how to use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider&lt;/code&gt; class to write tests that use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.NET 8.0 added the TimeProvider class to abstract date and time. It has a virtual method GetUtcNow() that sets the current time inside tests. It also has a non-testable implementation for production code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s play with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider&lt;/code&gt; by revisiting &lt;a href=&quot;/2021/05/10/WriteTestsThatUseDateTimeNow/&quot;&gt;how to write tests that use DateTime.Now&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Back in the day, we wrote two tests to validate expired credit cards. And we wrote an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt; interface to control time inside our tests. These are the tests we wrote:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FluentValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FluentValidation.TestHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TimeProviderTests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Look, ma! I&apos;m going back in time&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Look, ma! I&apos;m going back in time again&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ISystemClock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FixedDateClock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISystemClock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISystemClock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Using now to validate credit card expiration year and month...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt; that extended &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt; to freeze time inside our tests. The thing is, we don’t need them with .NET 8.0.&lt;/p&gt;

&lt;h2 id=&quot;1-use-timeprovider-instead-of-isystemclock&quot;&gt;1. Use TimeProvider instead of ISystemClock&lt;/h2&gt;

&lt;p&gt;Let’s get rid of our old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt; by making our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardValidator&lt;/code&gt; receive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider&lt;/code&gt; instead, like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// public CreditCardValidator(ISystemClock systemClock)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// After:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetUtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//var now = systemClock.GetLocalNow();&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Rest of the code here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider&lt;/code&gt; abstract class has the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetUtcNow()&lt;/code&gt; method to override the current UTC date and time. Also, it has the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LocalTimeZone&lt;/code&gt; property to override the local timezone. With this timezone, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetLocalNow()&lt;/code&gt; returns the “frozen” UTC time as a local time.&lt;/p&gt;

&lt;p&gt;If we’re working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task&lt;/code&gt;, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Delay()&lt;/code&gt; method to create a task that completes after, well, a delay. Let’s use the short delays in our tests to &lt;a href=&quot;/2023/05/29/SpeedingUpSomeTests/&quot;&gt;avoid making our tests slow&lt;/a&gt;. Nobody wants a slow test suite.&lt;/p&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider&lt;/code&gt;, we can control time inside our tests by injecting a fake. But for production code, let’s use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider.System&lt;/code&gt;. It uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTimeOffset.UtcNow&lt;/code&gt; under the hood.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1557767536-34e0d6e7086c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxNTg4MTM4Nw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;person holding glass ball&quot; /&gt;

&lt;figcaption&gt;Time from another perspective. Photo by &lt;a href=&quot;https://unsplash.com/@nunchakouy?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Jossuha Théophile&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/grayscale-photography-of-person-holding-glass-ball-JHjKNOPC3lc?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-use-faketimeprovider-instead-of-fixeddateclock&quot;&gt;2. Use FakeTimeProvider instead of FixedDateClock&lt;/h2&gt;

&lt;p&gt;We might be tempted to wrie a child class that extends &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider&lt;/code&gt;. But, let’s hold our horses. There’s an option for that too.&lt;/p&gt;

&lt;p&gt;Let’s rewrite our tests after that change in the signature of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardValidator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, let’s install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Extensions.TimeProvider.Testing&lt;/code&gt; NuGet package. It has a fake implementation of the time provider: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeTimeProvider&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here are our two tests using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeTimeProvider&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FluentValidation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FluentValidation.TestHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Time.Testing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TestingTimeProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//var when = new DateTime(2021, 01, 01);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//var clock = new FixedDateClock(when);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTimeOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakeTimeProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Look, ma! No more ISystemClock&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//var when = new DateTime(2021, 01, 01);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//var clock = new FixedDateClock(when);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTimeOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakeTimeProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Look, ma! I&apos;m going back in time&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeTimeProvider&lt;/code&gt; has two constructors. One without parameters sets the internal date and time to January 1st, 2000, at midnight. And another one that receives a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTimeOffset&lt;/code&gt;. That was the one we used in our two tests.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeTimeProvider&lt;/code&gt; has two helpful methods to change the internal date and time: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SetUtcNow()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Advance()&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SetUtcNow()&lt;/code&gt; receives a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTimeOffset&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Advance()&lt;/code&gt;, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeSpan&lt;/code&gt; to add it to the internal date and time.&lt;/p&gt;

&lt;p&gt;If we’re curious, this is the source code of &lt;a href=&quot;https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/Common/src/System/TimeProvider.cs&quot;&gt;TimeProvider&lt;/a&gt; and &lt;a href=&quot;https://github.com/dotnet/extensions/blob/e5e1c7c88f3232bb3a096990da52fe7bf8a76996/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs#L121C9-L129C6&quot;&gt;FakeTimeProvider&lt;/a&gt; from the official dotnet repository on GitHub.&lt;/p&gt;

&lt;p&gt;If we take a closer look at our tests, we’re “controlling” the time inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardValidator&lt;/code&gt;. But, we still have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.UtcNow&lt;/code&gt; when creating a credit card. For that, we can introduce a class-level constant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Now&lt;/code&gt;. But that’s an “exercise left to the reader.”&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to use the new .NET 8.0 abstraction to test time. We have the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeProvider&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeTimeProvider&lt;/code&gt;. We don’t need our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt; anymore.&lt;/p&gt;

&lt;p&gt;If you want to read more content, check &lt;a href=&quot;/2024/04/01/NET8FakeLogger/&quot;&gt;how to Test Logging Messages with FakeLogger&lt;/a&gt; and my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where we cover from what a unit test is, to fakes and mocks, to other best practices.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I applied at a FAANG and failed: Three interviewing lessons</title>
   <link href="https://canro91.github.io/2024/05/27/ApplyingToFaang/"/>
   <updated>2024-05-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/05/27/ApplyingToFaang</id>
   <content type="html">&lt;p&gt;Overconfidence killed all my chances of success.&lt;/p&gt;

&lt;p&gt;I applied for a role as a software engineer at a FAANG or MAGMA or insert-newest-acronym here.&lt;/p&gt;

&lt;p&gt;And I failed.&lt;/p&gt;

&lt;p&gt;I thought: “I have more than 10 years of experience. I’ve seen quite a lot.”&lt;/p&gt;

&lt;p&gt;A “short coding assessment” got me off guard. 80 minutes and 3 exercises made me feel like an impostor. An uppercut and 10-second countdown.&lt;/p&gt;

&lt;p&gt;I don’t want this post to be another “hiring is broken” and “life is unfair” post. So…&lt;/p&gt;

&lt;p&gt;If I could go back in time, this is what I’d tell myself before that coding assesment:&lt;/p&gt;

&lt;h2 id=&quot;1-review-data-structures-especially-those-you-dont-use-often&quot;&gt;1. Review data structures, especially those you don’t use often.&lt;/h2&gt;

&lt;p&gt;Take the time to review data structures. Lists, hash maps, queues, trees.&lt;/p&gt;

&lt;p&gt;Trees, is this you?&lt;/p&gt;

&lt;p&gt;I haven’t used trees since my data structure class back in university. And probably, I wouldn’t use them if I had passed the interview and joined.&lt;/p&gt;

&lt;p&gt;But, surprise, surprise. That was one of the questions.&lt;/p&gt;

&lt;h2 id=&quot;2-practice-using-a-timer-and-a-coding-editor-without-auto-completion&quot;&gt;2. Practice using a timer and a coding editor without auto-completion&lt;/h2&gt;

&lt;p&gt;I know it’s unrealistic. These days, we have IDEs with autocompletion and even AI at our fingertips.&lt;/p&gt;

&lt;p&gt;But MAGMAs insist on hiring using coding platforms without autocompletion. The old way.&lt;/p&gt;

&lt;p&gt;Since practicing a skill should be as “real” as possible, close your IDE and practice using a bare-bones text editor. And with a timer on.&lt;/p&gt;

&lt;h2 id=&quot;3-read-all-questions-first-i-know&quot;&gt;3. Read all questions first. I know!&lt;/h2&gt;

&lt;p&gt;Yeah, I wanted to be an A-student playing with the rules. I jumped right to the first question.&lt;/p&gt;

&lt;p&gt;50 minutes in and I had barely an answer for the first question. I had to decide between solving only one question or moving on and trying to solve another one. One and a half questions are better than only one, I guess.&lt;/p&gt;

&lt;p&gt;I could have nailed the second one first. It was way easier. And definitively, I could have solved the last two questions and skipped the first one. If only I had read all the questions first.&lt;/p&gt;

&lt;p&gt;Read all the questions and start with the easy ones. Old advice that I forgot.&lt;/p&gt;

&lt;p&gt;Voila! That’s what I’d tell myself before that coding assessment. Yeah, hiring is broken, but we have to go through gatekeepers. Or ditch our CVs and interviewing skills and build a place for ourselves.&lt;/p&gt;

&lt;p&gt;For more interview content, read &lt;a href=&quot;/2019/09/29/RemoteInterviewTips/&quot;&gt;remote interview types and tips&lt;/a&gt; and &lt;a href=&quot;/2021/11/22/CodingChallengeTips/&quot;&gt;ten tips to solve your next interview coding challenge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Starting out or already on the coding journey? &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;Join my free 7-day email course&lt;/a&gt; and refactor your coding career–I distill 10+ years of career lessons into 7 short emails.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy interviewing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Steal This 6-Step Reading Process To Retain More From Books</title>
   <link href="https://canro91.github.io/2024/05/13/HowToReadNonFictionBooks/"/>
   <updated>2024-05-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/05/13/HowToReadNonFictionBooks</id>
   <content type="html">&lt;p&gt;Reading more books isn’t the answer.&lt;/p&gt;

&lt;p&gt;In the last two or three years, I read dozens of non-fiction books. The truth is I don’t remember reading some of them, even when I took notes. I don’t even remember their front cover or why I decided to read them.&lt;/p&gt;

&lt;p&gt;I was reading to increase a book count. Pure FOMO. Without realizing it, I was trying to copy those YouTube videos: “I read &amp;lt;insert large number here&amp;gt; books, here’s what I learned.”&lt;/p&gt;

&lt;p&gt;After that realization, I adapted the slip-box or Zettelkasten method, described in &lt;a href=&quot;/2020/11/18/HowToTakeSmartNotes/&quot;&gt;How to Take Smart Notes&lt;/a&gt; by Sönke Ahrens, and other reading techniques into my own reading system.&lt;/p&gt;

&lt;p&gt;This is the six-step process I follow to read and retain more from non-fiction books:&lt;/p&gt;

&lt;h2 id=&quot;step-1-intention&quot;&gt;Step 1: Intention&lt;/h2&gt;

&lt;p&gt;Start by asking why you want to read a book.&lt;/p&gt;

&lt;p&gt;We don’t jump into a book with the same attitude if we’re just curious about a subject or want to answer a particular question.&lt;/p&gt;

&lt;p&gt;That intention will set the tone for our reading. And based on that, we could decide if we want to read the book from cover to cover or jump into a particular section.&lt;/p&gt;

&lt;h2 id=&quot;step-2-overview&quot;&gt;Step 2: Overview&lt;/h2&gt;

&lt;p&gt;Ask if the book you’re about to read can answer your questions or satisfy your curiosity.&lt;/p&gt;

&lt;p&gt;For that, understand the purpose of the book and its content. Use reviews, summaries, or anything else to understand the overall book content.&lt;/p&gt;

&lt;p&gt;Often book authors go on podcast tours to promote their books, and listening to those podcast episodes helps us understand what their books are about.&lt;/p&gt;

&lt;p&gt;A good experiment to try is to use ChatGPT or Copilot for this step. We could use any of the two to generate an executive summary of a book, for example.&lt;/p&gt;

&lt;h2 id=&quot;step-3-note&quot;&gt;Step 3: Note&lt;/h2&gt;

&lt;p&gt;Create a new place for your notes, either a text file or a piece of paper.&lt;/p&gt;

&lt;p&gt;For this step, &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;I use old plain-text files&lt;/a&gt;. Obsidian, Notion, or any note-taking app works here.&lt;/p&gt;

&lt;p&gt;For the new note, use the date and the book title as the note title. Then divide the note into two halves. The first half is for questions and connections, and the second half is for the actual notes.&lt;/p&gt;

&lt;p&gt;Also, link to the new book note from a book index and a subject index. The book index is a note that links to all books you’ve read. And the subject index is a note that references all other notes related to a specific subject. These notes work like “index” notes.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2024-05-13-HowToReadNonFictionBooks/HowIReadBooks.png&quot; alt=&quot;a rectangle divided into two halves&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;My note structure and links between notes&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;step-4-question&quot;&gt;Step 4: Question&lt;/h2&gt;

&lt;p&gt;Read the table of contents, introduction, and conclusion to look for the book’s structure and interesting topics.&lt;/p&gt;

&lt;p&gt;Also, skim through the book to find anything that grabs your attention: boxes, graphs, and tables.&lt;/p&gt;

&lt;p&gt;In the first half of your note, write questions you have about the subject and questions that arise after skimming the book.&lt;/p&gt;

&lt;p&gt;If you decide not to read the book from cover to cover, in the first half of your note, create an index of the chapters and sections to read or the ones to skip. Keep this index for future reference.&lt;/p&gt;

&lt;p&gt;I learned this idea of asking “pre-reading” questions from Jim Kwik’s Speed Reading program and the idea of a custom index from the post &lt;a href=&quot;https://every.to/superorganizers/surgical-reading-how-to-read-12-books-580014&quot;&gt;Surgical Reading: How to Read 12 Books at Once&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-5-read&quot;&gt;Step 5: Read&lt;/h2&gt;

&lt;p&gt;Then, read the book while taking note of interesting parts and quotes.&lt;/p&gt;

&lt;p&gt;Use the second half of your notes to write down those interesting bits. Avoid copying and pasting passages from the book, except for quotes. Use your own words instead.&lt;/p&gt;

&lt;p&gt;After every chapter, stop to recall the main ideas from that chapter.&lt;/p&gt;

&lt;p&gt;If you find the answer to any of your questions from the “Question” step, go back to the first half of your note and write those answers there.&lt;/p&gt;

&lt;h2 id=&quot;step-6-connections&quot;&gt;Step 6: Connections&lt;/h2&gt;

&lt;p&gt;While reading or after finishing a chapter, notice connections with other subjects. Ask yourself how that expands or contradicts anything else you’ve learned.&lt;/p&gt;

&lt;p&gt;Use the first half of the note to write these connections.&lt;/p&gt;

&lt;p&gt;If you’re familiar with the Zettelkasten method, to write your connections, we should use a separate set of notes: the permanent notes. For simplicity, I keep these connections in the same file but in the first half.&lt;/p&gt;

&lt;p&gt;This way, the next time you open your note, you will find your connections and critique first.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;The key to retaining more is reading actively. Don’t just pass from page to page. Read with intention, not to grow a book count. Read for answers and connections.&lt;/p&gt;

&lt;p&gt;The next time you’re about to jump into a new book, remember to set an intention and then capture and connect.&lt;/p&gt;

&lt;p&gt;Reading more books isn’t the answer. Reading for retention and action is.&lt;/p&gt;

&lt;p&gt;For more content, check my takeaways from &lt;a href=&quot;/2020/05/07/PragmaticThinkingAndLearning/&quot;&gt;Pragmatic Thinking and Learning&lt;/a&gt; and &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy reading!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>AI Won&apos;t Take Our Coding Jobs Yet. But The World Will Need Different Coders</title>
   <link href="https://canro91.github.io/2024/04/29/2034Predictions/"/>
   <updated>2024-04-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/04/29/2034Predictions</id>
   <content type="html">&lt;p&gt;On March 12th, 2024, the coding world went nuts.&lt;/p&gt;

&lt;p&gt;That day, Cognition Labs &lt;a href=&quot;https://twitter.com/cognition_labs/status/1767548763134964000?t=37X_HEozIj6xikxg-B8YrQ&amp;amp;s=08&quot;&gt;announced in their Twitter/X account&lt;/a&gt; the release of Devin, “the first AI software engineer.”&lt;/p&gt;

&lt;p&gt;That announcement made the coding world run in circles, screaming in desperation. It also triggered an interesting conversation with a group of colleagues and ex-coworkers.&lt;/p&gt;

&lt;p&gt;Here’s a summary of how a group of senior engineers viewed Devin and AI in general, and my own predictions for coding in the next 10 years.&lt;/p&gt;

&lt;h2 id=&quot;if-you-think-youre-out-youre-out&quot;&gt;If You Think You’re Out, You’re Out!&lt;/h2&gt;

&lt;p&gt;We all agreed that Devin and AI in general won’t take out jobs yet, but it will change the landscape for sure.&lt;/p&gt;

&lt;p&gt;This is when the meme “AI needs well-written and unambiguous requirements, so we’re safe” is true.&lt;/p&gt;

&lt;p&gt;One part of the group believed that software engineering, as we know it, would disappear in less than 10 years. They expected to see more layoffs and unemployment. They were also planning escape routes away from this industry.&lt;/p&gt;

&lt;p&gt;If you’re reading this from the future, a bit of context about layoffs:&lt;/p&gt;

&lt;p&gt;Around the 2020 pandemic, we enjoyed a boom in employment. We only needed “software engineer” as the title in our LinkedIn profiles to have dozens of recruiters offering “life-changing opportunities” every week.&lt;/p&gt;

&lt;p&gt;But, in 2023 and 2024, we experienced massive layoffs. Either we were laid off or knew someone in our inner circle who was. It was a crazy time: one job listing, hundreds of applicants, and radio silence after sending a CV.&lt;/p&gt;

&lt;p&gt;Most people claimed that AI took those jobs. Others claimed tech companies were so used to free money that went crazy hiring and then, when money wasn’t free anymore, they went crazy firing too. In any case, it was hard to get a new job in that season of layoffs.&lt;/p&gt;

&lt;p&gt;Ok, end of that detour and back to the AI story.&lt;/p&gt;

&lt;p&gt;While one part of the group was thinking of escaping routes, the other part believed the world would still need software engineers, at least, to oversee what AI does.&lt;/p&gt;

&lt;p&gt;We wondered if AI needs human oversight, what about the working conditions for future software engineers? Maybe will they come from developing countries, with an extremely low wage and poor working conditions to fix the “oops” of AI software engineers? And if unfortunate software engineers were already under those conditions, wouldn’t all future software developers (at least the ones still standing) face the same fate?&lt;/p&gt;

&lt;p&gt;Our conversation was divided into despair and change.&lt;/p&gt;

&lt;h2 id=&quot;the-world-will-need-a-different-type-of-coders&quot;&gt;The World Will Need a Different Type of Coders&lt;/h2&gt;

&lt;p&gt;In 2034, knowing programming and coding by itself won’t be enough.&lt;/p&gt;

&lt;p&gt;We will need to master a business domain or area of expertise and use programming in that context, mainly with AI as a tool.&lt;/p&gt;

&lt;p&gt;Rather than being mundane code monkeys, our role will look like Product Managers. The coding part will be automated with AI. We will work more as requirement gatherers and writers and prompt engineers. Soft skills will be even more important than ever. Essentially, we all will be engineering managers overseeing a group of Devin’s.&lt;/p&gt;

&lt;p&gt;We will see more Renaissance men and women, well-versed in different areas of knowledge, managing different AIs to achieve the goal of entire software teams.&lt;/p&gt;

&lt;p&gt;In the meantime, if somebody else writes requirements and we, software engineers, merely translate those requirements into code, we’ll be out of business sooner rather than later.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how Software Engineering will look like in 2034: more human interaction and business understanding to identify requirements for AI software engineers. No more zero-value tasks like manual testing, code generation, and pointless meetings. Yeah, I’m looking at you, SCRUM daily meetings. AI will handle it all. Hopefully.&lt;/p&gt;

&lt;p&gt;This is what &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;Programming was like in 2020&lt;/a&gt; and &lt;a href=&quot;/2024/03/18/AIToLaunchMyCourses/&quot;&gt;how I used AI to launch my courses in 2024&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yes, I know! Making predictions about the future is hard.&lt;/p&gt;

&lt;p&gt;“Open in 2034”&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two new LINQ methods in .NET 9: CountBy and Index</title>
   <link href="https://canro91.github.io/2024/04/15/NET9LinqMethods/"/>
   <updated>2024-04-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/04/15/NET9LinqMethods</id>
   <content type="html">&lt;p&gt;LINQ doesn’t get new features with each release of the .NET framework. It just simply works. This time, .NET 9 introduced two new LINQ methods: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountBy()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Index()&lt;/code&gt;. Let’s take a look at them.&lt;/p&gt;

&lt;h2 id=&quot;1-countby&quot;&gt;1. CountBy&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CountBy groups the elements of a collection by a key and counts the occurrences of each key. With CountBy, there’s no need to first group the elements of a collection to count its occurrences.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, let’s count all movies in our catalog by release year, of course, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountBy()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Forrest Gump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Armageddon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3.35f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pulp Fiction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;countByReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CountBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                              ^^^^^^^&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;countByReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1997: [2]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1994: [2]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1991: [1]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1998: [1]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1986: [1]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1988: [1]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountBy()&lt;/code&gt; returns a collection of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KeyValuePair&lt;/code&gt; with the key in the first position and the count in the second one.&lt;/p&gt;

&lt;p&gt;By the way, if that Console application doesn’t look like one, it’s because we’re using &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;three recent C# features&lt;/a&gt;: the Top-level statements, records, and global using statements.&lt;/p&gt;

&lt;p&gt;Before .NET 9.0, we needed to use &lt;a href=&quot;/2022/05/30/HowToUseLinqGroupBy/&quot;&gt;GroupBy() with a second parameter&lt;/a&gt; to transform each group, like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;countByReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GroupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountBy()&lt;/code&gt; has the same spirit of &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;DistinctBy, MinBy, MaxBy, and other LINQ methods from .NET 6.0&lt;/a&gt;. With these methods, we apply an action direcly on a collection using a key selector. We don’t need to filter or group a collection first to apply that action.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1440404653325-ab127d49abc1?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMDk3NzY4Mw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;Cinematographer&apos;s room&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@imnoom?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Noom Peerapong&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/two-reels-2uwFEAGUm6E?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-index&quot;&gt;2. Index&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Index projects every element of a collection alongside its position in the collection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s “index” our catalog of movies,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                                    ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 0: [Titanic]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 1: [The Fifth Element]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 2: [Terminator 2]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 3: [Avatar]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 4: [Platoon]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 5: [My Neighbor Totoro]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unlike &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountBy()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Index()&lt;/code&gt; returns named tuples. It returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;(int Index, TSource Item)&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before, we had to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select()&lt;/code&gt; overload or roll our own extension method. In fact, this is one of the &lt;a href=&quot;/2022/12/16/HelperMethodsOnCollections/&quot;&gt;helpful extension methods I use to work with collections&lt;/a&gt;. But I call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerated()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we take a look at the &lt;a href=&quot;https://github.com/dotnet/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/Index.cs&quot;&gt;Index source code on GitHub&lt;/a&gt;, it’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; loop with a counter in its body. Nothing fancy!&lt;/p&gt;

&lt;p&gt;Voilà! Those are two new LINQ methods in .NET 9.0: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CountBy()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Index()&lt;/code&gt;. It seems the .NET team is bringing to the standard library the methods we needed to roll ourselves before.&lt;/p&gt;

&lt;p&gt;To learn about LINQ and other methods, check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;quick guide to LINQ&lt;/a&gt;, &lt;a href=&quot;/2022/06/13/LinqMistakes/&quot;&gt;five common LINQ mistakes and how to fix them&lt;/a&gt;, and &lt;a href=&quot;/2022/07/11/LinqDistinctBySourceCode/&quot;&gt;peeking into LINQ DistinctBy source code&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Test Logging Messages with FakeLogger</title>
   <link href="https://canro91.github.io/2024/04/01/NET8FakeLogger/"/>
   <updated>2024-04-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/04/01/NET8FakeLogger</id>
   <content type="html">&lt;p&gt;Starting with .NET 8.0, we have a better alternative for testing logging and logging messages. We don’t need to roll our own mocks anymore. Let’s learn how to use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; inside our unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.NET 8.0 introduces FakeLogger, an in-memory logging provider designed for unit testing. It provides methods and properties, such us LatestRecord, to inspect the log entries recorded inside unit tests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s revisit our post on &lt;a href=&quot;/2022/12/04/TestingLoggingAndLogMessages/&quot;&gt;unit testing logging messages&lt;/a&gt;. In that post, we used a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mock&amp;lt;ILogger&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; to verify that we logged the exception message thrown inside a controller method. This was the controller we wanted to test,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FakeLogger.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[controller]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerBase&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IClientService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IClientService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                               &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_clientService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpPost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPostRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Imagine that this service does something interesting...&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Something horribly wrong happened. ClientId: [{clientId}]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//      ^^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Logging things like good citizens of the world...&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BadRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Just for reference...Nothing fancy here&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IClientService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyPostRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1624782460910-df75c1f7a0bc?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMTA1MjUwMw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;A pile of cut wood logs&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@jatin_graphix?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Jatin Jangid&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/brown-and-gray-brick-wall-1f0DB1u7p8Q?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;1-creating-a-fakelogger&quot;&gt;1. Creating a FakeLogger&lt;/h2&gt;

&lt;p&gt;Let’s test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PostAsync()&lt;/code&gt; method, but this time let’s use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; instead of a &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;mock with Moq&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt;, let’s install the NuGet package: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Extensions.Diagnostics.Testing&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;Here’s the test,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FakeLogger.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Logging.Testing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//    ^^^^^&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FakeLogger.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingControllerTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PostAsync_Exception_LogsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IClientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Expected exception...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 3...2...1...Boom...&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Look, ma! No mocks here...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                                &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyPostRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Warning!!!&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//var expected = $&quot;Something horribly wrong happened. ClientId: [{clientId}]&quot;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//Assert.AreEqual(expected, fakeLogger.LatestRecord.Message);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//       ^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Do not expect exactly the same log message thrown from PostAsync()&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// Even better:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyWasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We needed a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Extensions.Logging.Testing&lt;/code&gt;. Yes, that’s different from the NuGet package name.&lt;/p&gt;

&lt;p&gt;We wrote &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new FakeLogger&amp;lt;SomethingController&amp;gt;()&lt;/code&gt; and passed it around. That’s it.&lt;/p&gt;

&lt;h2 id=&quot;2-asserting-on-fakelogger&quot;&gt;2. Asserting on FakeLogger&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LatestRecord&lt;/code&gt; property that captures the last entry we logged. Its type is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeLogRecord&lt;/code&gt; and contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Level&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Message&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exception&lt;/code&gt;. And if no logs have been recorded, accessing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LatestRecord&lt;/code&gt; will throw an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InvalidOperationException&lt;/code&gt; with the message “No records logged.”&lt;/p&gt;

&lt;p&gt;But, for &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;the Assert part&lt;/a&gt; of our test, we followed the lesson from our previous post on &lt;a href=&quot;/2022/12/04/TestingLoggingAndLogMessages/&quot;&gt;testing logging messages&lt;/a&gt;: &lt;strong&gt;do not expect exact matches of logging messages in assertions&lt;/strong&gt;. Otherwise, any change in the structure of our logging messages will make our test break, even if the underlying business logic remains unchanged.&lt;/p&gt;

&lt;p&gt;Instead of expecting exact matches of the logging messages, we wrote an extension method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyWasCalled()&lt;/code&gt;. This method receives a log level and a substring as parameters. Here it is,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VerifyWasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasLogRecord&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collector&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSnapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasLogRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Expected log entry with level [Warning] and message containing &apos;Something else&apos; not found.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Log entries found:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// [15:49.229, error] Something horribly wrong happened. ClientId: [123456]&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptionMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Expected log entry with level [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;] and message containing &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos; not found.&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Log entries found:&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSnapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssertFailedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptionMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collector&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetSnapshot()&lt;/code&gt; to grab a reference to the collection of log entries recorded. Then, we checked we had a log entry with the expected log level and message. Next, we wrote a handy exception message showing the log entries recorded.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to write tests for logging messages using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; instead of mocks.&lt;/p&gt;

&lt;p&gt;If we only want to create a logger inside our tests without asserting anything on it, let’s use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NullLogger&amp;lt;T&amp;gt;&lt;/code&gt;. But, if we want to check we’re logging exceptions, like good citizens of the world, let’s use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeLogger&amp;lt;T&amp;gt;&lt;/code&gt; and avoid tying our tests to details like the log count and the exact log messages. That makes our tests harder to maintain. In any case, we can roll mocks to test logging.&lt;/p&gt;

&lt;p&gt;If you want to read more about unit testing, check &lt;a href=&quot;/2022/12/01/TestingHttpClient/&quot;&gt;How to write tests for HttpClient using Moq&lt;/a&gt;, &lt;a href=&quot;/2022/12/03/TestingAspNetAuthorizationFilters/&quot;&gt;how to test an ASP.NET Authorization Filter&lt;/a&gt; and &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;how to write good unit tests: Use simple test values&lt;/a&gt;. Don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where we cover from what a unit test is, to fakes and mocks, to other best practices.&lt;/p&gt;

&lt;p&gt;Happy testing!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I used AI to launch my new testing course</title>
   <link href="https://canro91.github.io/2024/03/18/AIToLaunchMyCourses/"/>
   <updated>2024-03-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/03/18/AIToLaunchMyCourses</id>
   <content type="html">&lt;p&gt;I ran an experiment. Maybe it was fear of missing out. I decided to use AI to help me launch a new course. This is how I used Copilot and the prompts I used.&lt;/p&gt;

&lt;p&gt;I got this idea after watching one of Brent Ozar’s &lt;a href=&quot;https://youtu.be/JtJSecikVhk?si=CoW7_JpjXP6o09rN&amp;amp;t=607&quot;&gt;Office Hours videos on YouTube&lt;/a&gt; where he shared he keeps ChatGPT opened all the time and uses it as a junior employee. I decided to run a similar experiment, but for launching a new course on unit testing, one of my favorite subjects.&lt;/p&gt;

&lt;h2 id=&quot;1-lesson-content-and-materials&quot;&gt;1. Lesson content and materials&lt;/h2&gt;

&lt;p&gt;I planned the lesson content and recorded and edited all video lessons myself. #madebyahuman&lt;/p&gt;

&lt;p&gt;For the editing part, I used &lt;a href=&quot;https://podcast.adobe.com/enhance#&quot;&gt;Adobe Podcast&lt;/a&gt; to remove background noise. My neighbor’s dog started to bark every time I hit record. And the other day at home, somebody made a smoothie with a loud blender while I was recording. Arrrggg! The only downside is that my voice sounds auto-tuned, especially in word endings.&lt;/p&gt;

&lt;p&gt;I used Copilot for its convenience. I don’t need to create an account. Even I made Microsoft Edge open Copilot as the default tab. I only need to press the Windows key, type “Edge,” and I’m right there.&lt;/p&gt;

&lt;p&gt;These are the prompts I used.&lt;/p&gt;

&lt;h3 id=&quot;generate-a-list-of-title-ideas-for-my-course&quot;&gt;Generate a list of title ideas for my course&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;You’re an expert on course creation, programming, and SEO, give me a list of titles for a course to teach &lt;em&gt;insert subject here&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;rewrite-my-draft-for-a-landing-page&quot;&gt;Rewrite my draft for a landing page&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Now you’re an expert on online writing, SEO, marketing, and copywriting, please help me improve this landing page for an online course to increase sales and conversions. Make sure to use a friendly and conversational tone.&lt;/p&gt;

  &lt;p&gt;This is my landing page:&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;insert landing page here&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;turn-a-landing-page-into-a-script-for-an-introductory-video&quot;&gt;Turn a landing page into a script for an introductory video&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Now turn that last version of the landing page into a list of paragraphs and sentences I can use to create a PowerPoint presentation. Keep it short and to the point. Use only 10 paragraphs. I will turn each paragraph into a slide&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1677756119517-756a188d2d94?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwOTA2OTc0OQ&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;Cute tiny little robots working in a futuristic soap factory&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@siderius_creativ?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Gerard Siderius&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/a-robot-holding-a-gun-next-to-a-pile-of-rolls-of-toilet-paper-YeoSV_3Up-k?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-online-marketing&quot;&gt;2. Online Marketing&lt;/h2&gt;

&lt;p&gt;Once I got all the lesson content and a landing page ready, I moved to the promotion part.&lt;/p&gt;

&lt;p&gt;These are the prompts I used.&lt;/p&gt;

&lt;h3 id=&quot;write-an-email-inviting-readers-to-buy-this-new-course&quot;&gt;Write an email inviting readers to buy this new course&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;You’re an expert on copywriting and email marketing, give me &lt;em&gt;n&lt;/em&gt; ideas for a short email to invite a reader who already &lt;em&gt;took some action&lt;/em&gt; to buy my new video course: &lt;em&gt;insert course name here&lt;/em&gt;. In that course, I &lt;em&gt;insert brief description here&lt;/em&gt;. Offer a promo code for a limited time. Use friendly and engaging language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;write-a-call-to-action-for-my-posts&quot;&gt;Write a call to action for my posts&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;You’re an expert copywriter, give me &lt;em&gt;n&lt;/em&gt; ideas for a two-sentence paragraph to promote my new course &lt;em&gt;insert course name here&lt;/em&gt;. I’d like to use that paragraph at the end of posts on my blog to invite my readers to join the course. Use a friendly and conversational tone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;write-a-launching-post-on-linkedin&quot;&gt;Write a launching post on LinkedIn&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;You’re an expert on copywriting, LinkedIn, and personal branding. Give me &lt;em&gt;n&lt;/em&gt; ideas for a LinkedIn post to promote my new course &lt;em&gt;insert title here&lt;/em&gt; based on the landing page of the course. Use a friendly and conversational tone.&lt;/p&gt;

  &lt;p&gt;This is the course landing page of the course:&lt;/p&gt;

  &lt;p&gt;&lt;em&gt;insert landing page here&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nothing fancy. I followed the pattern: “Act as X, do Y for me based on some input. This is the input.”&lt;/p&gt;

&lt;p&gt;I don’t use the exact same words Copilot gives me. I change the words I don’t use to make it sound like me.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how I used AI, especially Copilot, to help me launch my new testing course. On a Saturday morning, I ended up with a landing page and the script for an intro video for the course. What a productive morning!&lt;/p&gt;

&lt;p&gt;I use AI the same way Jim Kwik, the brain coach, recommends in one of &lt;a href=&quot;https://www.youtube.com/watch?v=bKFeYWVpFh8&quot;&gt;his YouTube videos&lt;/a&gt;: “AI (artificial intelligence) to enhance HI (human intelligence), not to replace it.” I don’t want AI to take out the pleasure of doing what I like to do. It’s my assistant.&lt;/p&gt;

&lt;p&gt;In case you’re curious, this is the end result: &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting201&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more productivity content (but not AI-related), check &lt;a href=&quot;/2023/11/27/ColorsAsVisualAid/&quot;&gt;how to color a website based on its URL&lt;/a&gt; and &lt;a href=&quot;/2023/11/13/DeclutteringUBlockOrigin/&quot;&gt;how to declutter sites with uBlock Origin filters&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Extreme Ownership: How U.S. Navy SEALs Lead and Win. Takeaways</title>
   <link href="https://canro91.github.io/2024/03/04/ExtremeOwnershipTakeaways/"/>
   <updated>2024-03-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/03/04/ExtremeOwnershipTakeaways</id>
   <content type="html">&lt;p&gt;By the title, I guess you infer this isn’t a book about software engineering or programming. It’s about leadership. By any means, I’m advocating for war. But I think the military has to teach a lot about management and leadership. For years, they have been leading large organizations with complex tasks in changing environments. That sound a lot like software engineering, right?&lt;/p&gt;

&lt;p&gt;Extreme Ownership is a book about the stories of two Navy SEALs officers and the lessons they learned while in service. In every chapter, they tell a story about their time in service, the leadership principle behind it, and its application to the corporate world. It’s an easy read. Probably, you could finish it in a weekend, too.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1534724364725-325f10a8e182?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4MjcyNDQ2OA&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;A group of soldier saluting&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/es/@jeffreyflin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Jeffrey F Lin&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/tT3LjNT-Oq8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;These are some of my takeaways.&lt;/p&gt;

&lt;h2 id=&quot;extreme-ownership--leadership&quot;&gt;Extreme Ownership &amp;amp; Leadership&lt;/h2&gt;

&lt;p&gt;Extreme Ownership is the principle that &lt;strong&gt;a leader “owns” or is responsible for everything that happens to the mission or his team&lt;/strong&gt;. Even to take the blame when things go wrong. “The leader is truly and ultimately responsible for everything.”&lt;/p&gt;

&lt;p&gt;Leaders, who accept their responsibility when things go wrong, inspire respect and trust. They show and teach that attitude to the team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A leader is responsible even for the underperformers.&lt;/strong&gt; His job is to train and mentor them to level up their skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A leader should lead “up” too.&lt;/strong&gt; He should speak up and ask questions when things could be better. He is also responsible for the communication with his own leaders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A leader should create a simple plan and make sure everyone understands it clearly&lt;/strong&gt;. A leader should first understand and believe in the mission. And when priorities change, he should communicate those changes and pass “situational awareness.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A leader should put a system and mentor people&lt;/strong&gt; so he can be the “tactical genius” looking at the bigger picture.&lt;/p&gt;

&lt;p&gt;Voilà! Those are some of my takeaways. The book uses memorable stories to tell these principles. I like how the authors extrapolated the principles they learned while on duty to the corporate world. It turns out that the authors, as SEAL officers, had to deal with some of the same bureaucracy from the corporate world too. Even PowerPoint presentations! PowerPoint presentations! I didn’t see that coming.&lt;/p&gt;

&lt;p&gt;As someone who influences decision-making inside teams, I enjoyed the book, and I will take lots of these ideas into my daily activities.&lt;/p&gt;

&lt;p&gt;I’d like to close with this quote from the book: “there are no bad teams, only bad leaders.”&lt;/p&gt;

&lt;p&gt;After reading this book, I connected the dots with some of the &lt;a href=&quot;/2022/12/17/LessonsOnAFailedProject/&quot;&gt;lessons I learned after a failed project&lt;/a&gt;. I needed Extreme Onwership in that project. Too late.&lt;/p&gt;

&lt;p&gt;If you want to read my takeaways from proramming books, read &lt;a href=&quot;/2022/10/17/UnitTestingPrinciplesPracticesTakeaways/&quot;&gt;Unit Testing Principles, Practices, and Patterns&lt;/a&gt; and &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;Hands-on Domain-Driven Design with .NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy reading!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Notebooks, Learning, and KPIs</title>
   <link href="https://canro91.github.io/2024/02/19/MondayLinks/"/>
   <updated>2024-02-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/02/19/MondayLinks</id>
   <content type="html">&lt;p&gt;Five interesting reads from the past month.&lt;/p&gt;

&lt;h2 id=&quot;lab-notebooks&quot;&gt;Lab Notebooks&lt;/h2&gt;

&lt;p&gt;I like the idea of keeping “lab” notebooks, especially to record the thought process for consulting clients. I’m a big fan of plain text. But the article recommends using pen and paper.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://sambleckley.com/writing/lab-notebooks.html&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;git-things&quot;&gt;Git Things&lt;/h2&gt;

&lt;p&gt;Git and Version Control are one of the subjects we have to learn ourselves. I prefer &lt;a href=&quot;/2022/12/19/LessonsAsReviewer/&quot;&gt;short Pull Requests&lt;/a&gt; with only one or two commits. I name my commits with a long and descriptive title that becomes a good title on PR descriptions.&lt;/p&gt;

&lt;p&gt;This article shows “tactics” to better use Git. I really like these two:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;start by writing the commit message before the actual work and then amending it. Commit Message Driven Development I guess, to follow the XDD trend&lt;/li&gt;
  &lt;li&gt;splitting refactorings into two commits: changing the API and then changing the calling sites&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://matklad.github.io/2023/12/31/git-things.html#Git-Things&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1581342997365-9e7cadb47edb?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNDkwNzM5Ng&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;Teacher and student sitting on a chair&quot; /&gt;

&lt;figcaption&gt;It&apos;s never too late to learn about learning...Photo by &lt;a href=&quot;https://unsplash.com/@bostonpubliclibrary?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Boston Public Library&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/man-and-woman-sitting-on-chair-pfyd9cSH5Ac?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;10-things-software-developers-should-learn-about-learning&quot;&gt;10 Things Software Developers Should Learn about Learning&lt;/h2&gt;

&lt;p&gt;This article contains ten findings, backed by research, about learning and software engineering. Totally worth reading if you’re into learning about learning.&lt;/p&gt;

&lt;p&gt;These are some of my favorite lessons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“Expert programmers may have low or high working memory capacity but it is the contents of their long-term memory that make them experts.”&lt;/li&gt;
  &lt;li&gt;“Expert developers can reason at a higher level by having memorized (usually implicitly, from experience) common patterns in program code, which frees up their cognition.”&lt;/li&gt;
  &lt;li&gt;“The brain needs rest to consolidate the new information that has been processed so that it can be applied to new problems.”&lt;/li&gt;
  &lt;li&gt;“There is a key distinction between a beginner who has never learned the details and thus lacks the memory connections, and an expert who has learned the deeper structure but searches for the forgotten fine details.”&lt;/li&gt;
  &lt;li&gt;“There is no reliable correspondence between problem-solving in the world of brain teasers and problem-solving in the world of programming. If you want to judge programming ability, assess programming ability.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don’t want to read the whole article, jump to the Summary section at the end.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cacm.acm.org/magazines/2024/1/278891-10-things-software-developers-should-learn-about-learning/fulltext&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-most-software-engineering-kpis-are-bs-and-what-to-do-about-it-in-2024&quot;&gt;Why Most Software Engineering KPIs are BS and What to Do About it in 2024&lt;/h2&gt;

&lt;p&gt;Does your team measure story points too? The truth is we don’t know how to measure our work. I worked with a company that measured the percentage of completed tasks. It was a killing pressure. And once we have a metric, we start to game it. On the last day of the sprint, we saw people removing tasks so as not to mess with the final percentage.&lt;/p&gt;

&lt;p&gt;From the article: &lt;em&gt;“Engineering leaders (and sometimes non-technical executives) have often made the poor choice to measure output rather than impact”&lt;/em&gt;. The author recommends: &lt;em&gt;“separate engineering health metrics to KPIs.”&lt;/em&gt; This is how the team feels vs the business impact of the work done.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jamesyorston.co.uk/articles/most_engineering_kpis_are_bs&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;27-years-ago-steve-jobs-said-the-best-employees-focus-on-content-not-process&quot;&gt;27 Years Ago, Steve Jobs Said the Best Employees Focus on Content, Not Process.&lt;/h2&gt;

&lt;p&gt;This article tells Steve Jobs’s opinion on office politics and bureaucracy and how often we think “paperwork” is the real work. Process vs Output. Also, this article shares how to keep the best team members around, those who are good at identifying the output.&lt;/p&gt;

&lt;p&gt;From the article: &lt;em&gt;“Success is rarely based solely on process. Success mostly comes down to what your business accomplishes.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Voilà! What are the KPIs of your team? Do you measure story points or percentage of completed vs planned tasks? Until next Monday Links.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This Project Taught Me More About Leadership Than Programming: Two Postmortem Lessons</title>
   <link href="https://canro91.github.io/2024/02/05/LessonsOnAFinishedProject/"/>
   <updated>2024-02-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/02/05/LessonsOnAFinishedProject</id>
   <content type="html">&lt;p&gt;Leadership and communication are more important than coding for the success of a project.&lt;/p&gt;

&lt;p&gt;Last year, I worked as an independent contractor and engaged in short projects with an American client. This project was a six-month effort to bring group events, like weddings, conferences, and retreats, to a property management system.&lt;/p&gt;

&lt;p&gt;This was one of the projects that convinced me that unclear expectations and poor communication kill any software project, not libraries and tools.&lt;/p&gt;

&lt;p&gt;These are the two lessons I learned from this project.&lt;/p&gt;

&lt;h2 id=&quot;lesson-1-have-uncomfortable-conversations-earlier&quot;&gt;Lesson 1: Have uncomfortable conversations earlier&lt;/h2&gt;

&lt;p&gt;Inside our team, we all started to notice “certain” behavior in a team member.&lt;/p&gt;

&lt;p&gt;He delayed &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;Pull Requests&lt;/a&gt; for minor things, interrupted meetings with off-topic questions, and asked for 100% detailed and distilled assignments.&lt;/p&gt;

&lt;p&gt;Even once, he made last-minute changes, blocking other team members before a deadline. It wasn’t a &lt;a href=&quot;/2023/09/04/AgainstMassiveUnrequestedRefactorings/&quot;&gt;massive unrequested refactoring&lt;/a&gt;, but it frustrated some people.&lt;/p&gt;

&lt;p&gt;From the outside, it might have appeared he was acting to jeopardize the team’s progress.&lt;/p&gt;

&lt;p&gt;It was time to virtually sit and talk to him. But nobody did it. And things started to get uncomfortable, eventually escalating the situation to upper management, leading to a team reorganization. There was “no chemistry with the team.”&lt;/p&gt;

&lt;p&gt;An empathetic conversation could have clarified this situation. Maybe the “problematic” team member had good intentions, but the team misinterpreted them. Who knows?&lt;/p&gt;

&lt;p&gt;This whole situation taught me to have uncomfortable conversations earlier.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1604881988758-f76ad2f7aac1?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNTk1OTM1MA&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Woman in black long sleeves holding a mug&quot; /&gt;

&lt;figcaption&gt;I hope that&apos;s not an uncomfortable conversation...Photo by &lt;a href=&quot;https://unsplash.com/@priscilladupreez?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Priscilla Du Preez&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/woman-in-black-long-sleeve-shirt-holding-black-ceramic-mug-K8XYGbw4Ahg?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;lesson-2-once-you-touch-it-you-own-it&quot;&gt;Lesson 2: Once you touch it, you own it&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“You only have to add your changes to this existing component. It’s already working.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I bet you have heard that, too. That was what our Product Owner told us to extend an existing feature. We needed to import an Excel file with a list of guests into a group event.&lt;/p&gt;

&lt;p&gt;The next thing we knew was that the already-working component had issues. &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;The original team was laid off&lt;/a&gt;, and we couldn’t get our questions answered or count on them to fix those issues. We were in the dark.&lt;/p&gt;

&lt;p&gt;What was a simple coding task turned out to be a longer one. Weeks later, we were still fixing issues and closing tickets. Nothing related to our task.&lt;/p&gt;

&lt;p&gt;Before starting to work on top of an “already-working” feature, I learned to test it and give a list of existing issues. Otherwise, those existing issues will appear as bugs in our changes. And people will start asking questions: &lt;em&gt;“Why are you taking so much time on this? It’s a simple task. It was already working.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After that time, I realized something similar had happened in every job I’ve been in. I didn’t see that coming in this project.&lt;/p&gt;

&lt;p&gt;Lesson learned! Once you touch it, you own it!&lt;/p&gt;

&lt;p&gt;Voilà! These are the lessons I learned from this project: have uncomfortable conversations and test already-working features. Also, this project made me think it’s better to hire a decent developer who can be mentored and coached than a “rock star” who can’t get along with the team.&lt;/p&gt;

&lt;p&gt;For more career lessons, check &lt;a href=&quot;/2019/08/19/FiveLessonsAfterFiveYears/&quot;&gt;five lessons from my first five years as a software engineer&lt;/a&gt;, &lt;a href=&quot;/2022/12/17/LessonsOnAFailedProject/&quot;&gt;three lessons I learned after a “failed” project&lt;/a&gt; and &lt;a href=&quot;/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer/&quot;&gt;things I wish I knew before working as a software engineer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Refactor your coding career with &lt;a href=&quot;https://imcsarag.gumroad.com/l/careerlessonsfromthetrenches&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;7DayEmailCourse-Footer&quot; data-goatcounter-title=&quot;7-Day Email Course: Footer&quot;&gt;my free 7-day email course&lt;/a&gt;. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: NDC Copenhagen</title>
   <link href="https://canro91.github.io/2024/01/22/MondayLinks/"/>
   <updated>2024-01-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/01/22/MondayLinks</id>
   <content type="html">&lt;p&gt;These are four NDC conferences from the past Copenhagen edition.&lt;/p&gt;

&lt;h2 id=&quot;iron-man-or-ultron-is-ai-here-to-help-us-or-hurt-us&quot;&gt;Iron Man or Ultron: Is AI here to help us or hurt us?&lt;/h2&gt;

&lt;p&gt;“We want our AI’s to be like an IronMan suite. Not like Ultron. If IronMan does something cool in his suit, he gets the credit for it. If IronMan is not in the suit, then the suit is just this empty shell”&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/RDVKl-27g9M?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;is-everything-difficult-or-is-it-just-me&quot;&gt;Is everything difficult, or is it just me?&lt;/h2&gt;

&lt;p&gt;This is a walkthrough of some mental health issues we all could face while working on a knowledge job. From Anxiety to Sleep. This is a subject we all should put on the table and talk about without any shame.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/n8YSjBEnf0Q?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;7-things-technical-leaders-can-learn-from-disney-princesses&quot;&gt;7 Things Technical Leaders Can Learn from Disney Princesses&lt;/h2&gt;

&lt;p&gt;I really liked this one. I never thought that Disney princesses could teach us about leadership. I have to confess that I skipped the singing parts from this talk. These are some of the main points:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“Don’t turn too many knobs at once…don’t expect immediate results when dealing with teams of people”&lt;/li&gt;
  &lt;li&gt;“Lean on your own self-worth and validation because the higher up in leadership you get the less external validation there will be for you”&lt;/li&gt;
  &lt;li&gt;“A lot of great leaders don’t have superpowers. They’re just generalists.” Instead, they can unlock other people’s potential. “You’re role is a catalyst.”&lt;/li&gt;
  &lt;li&gt;“Find support mechanisms when you move into leadership. This may be coaches, mentors, support groups”&lt;/li&gt;
  &lt;li&gt;“If you’re passing on orders, you’re not leading, you’re just managing”&lt;/li&gt;
  &lt;li&gt;“There’s no such a thing as a perfect team.” “Great leadership is about figuring out how to make teams from of a group of people”&lt;/li&gt;
  &lt;li&gt;“One of your main things is to make crystal-clear and sure that the agendas between your primary and secondary teams are aligned”&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/Yrd8dmAu9PY?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;how-work-works&quot;&gt;How Work Works&lt;/h2&gt;

&lt;p&gt;This talk uses Queue Theory to tune how complex organization works. These are some of the main points:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“IaaS platforms are M/M/c where c approaches to infinite”&lt;/li&gt;
  &lt;li&gt;Recommended books: Continous Delivery, Building Microservices, Building Evolutionary Architectures, Principles of Product Development Flow, Accelerate, Team Topologies&lt;/li&gt;
  &lt;li&gt;“Change request boards predict low performance”&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/qT6WY051nY4?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Voilà! Another Monday Links episode. Do you AI as part of your daily work? What leadership lessons can you share?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Best of 2023</title>
   <link href="https://canro91.github.io/2024/01/08/BestOf2023/"/>
   <updated>2024-01-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2024/01/08/BestOf2023</id>
   <content type="html">&lt;p&gt;In 2023, I realized that I’ve been &lt;a href=&quot;/2023/07/18/FiveYearsOfBlogging/&quot;&gt;blogging for five years&lt;/a&gt;. &lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;I started this blog&lt;/a&gt; to keep some notes online. Since then, I’ve used my blog to share my learned lessons, answer questions, and rant out loud. I consider my blog as &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;my time capsule&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last year, I wrote 23 posts. I tried to write one post every other week.&lt;/p&gt;

&lt;p&gt;I wrote a series of posts about &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;NullReferenceException&lt;/a&gt;, “the billion dollar mistake,” and expanded it into &lt;a href=&quot;https://www.educative.io/courses/mastering-nullreference-exception-prevention-in-c-sharp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a mini-course on Educative&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I updated my ASP.NET posts to use .NET 6.0, the most recent LTS version the last year.&lt;/p&gt;

&lt;p&gt;In one of my client’s projects, I worked with ORMLite. I wrote about &lt;a href=&quot;/2023/06/26/PassDataTableOrmLite/&quot;&gt;passing a datatable as parameter&lt;/a&gt; and &lt;a href=&quot;/2023/10/16/SubqueriesAndOrmLite/&quot;&gt;joining to subqueries&lt;/a&gt;. I was too lazy to write SQL queries by hand for those two edge cases. Also, I shared &lt;a href=&quot;/2023/10/30/LessonsOnOrmLite/&quot;&gt;five lessons I learned while working with ORMLite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I got an interesting question through &lt;a href=&quot;/contact&quot;&gt;my contact page&lt;/a&gt;. I could shared my advice on &lt;a href=&quot;/2023/07/24/AdviceToStartAnUltralearningProject/&quot;&gt;how to start an ultralearning project&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;five-posts-you-read-the-most&quot;&gt;Five posts you read the most&lt;/h2&gt;

&lt;p&gt;These are the five posts I wrote in 2023 you read the most. In case you missed any of them, here they are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;Goodbye, NullReferenceException: What it is and how to avoid it&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/03/20/UseOptionInsteadOfNull/&quot;&gt;Goodbye, NullReferenceException: Option and LINQ&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;Goodbye, NullReferenceException: Nullable Operators and References&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/08/07/TooManyLayers/&quot;&gt;Too many layers: My take on Queries and Layers&lt;/a&gt;. While working with one of my clients, I had to write a lot of response objects and mapping methods to implement read-only API endpoints. This post contains my thoughts on taking layering to the extreme.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/04/03/SeparateStateIntoSeparateObjects/&quot;&gt;Goodbye, NullReferenceException: Separate State in Separate Objects&lt;/a&gt;. This is how to apply the “make illegal state unrepresentable” mantra to avoid the NullReferenceException.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Last year, I continued working as an independent contractor and content writer. I continued writing for &lt;a href=&quot;https://www.alachisoft.com/blogs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NCache official blog&lt;/a&gt;. I started offering onsite courses and training in my city. Unfortunately, I saw a lot of coworkers &lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;being laid off&lt;/a&gt;. I hope they all had found “greener pastures.”&lt;/p&gt;

&lt;p&gt;Voilà! That’s my 2023 in review and your five favorite posts. I hope you enjoyed them as much as I did writing them.&lt;/p&gt;

&lt;p&gt;If any of my posts have helped you and you want to support my work, &lt;a href=&quot;https://www.educative.io/profile/view/5684280228839424&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;check my courses on Educative&lt;/a&gt;, &lt;a href=&quot;https://imcsarag.gumroad.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;download my ebooks&lt;/a&gt; and &lt;a href=&quot;https://imcsarag.gumroad.com/l/buymeacoffee&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;buy me a coffee&lt;/a&gt; on my Gumroad page.&lt;/p&gt;

&lt;p&gt;Don’t miss my &lt;a href=&quot;/2022/01/10/BestOf2021/&quot;&gt;best of 2021&lt;/a&gt; and &lt;a href=&quot;/2023/01/09/BestOf2022/&quot;&gt;best of 2022&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading, and happy coding in 2024!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to color a website based on its URL. A visual aid and time saver</title>
   <link href="https://canro91.github.io/2023/11/27/ColorsAsVisualAid/"/>
   <updated>2023-11-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/11/27/ColorsAsVisualAid</id>
   <content type="html">&lt;p&gt;These days, I spent a while debugging an issue. After a couple of minutes of scratching my head, I realized I was looking at log entries in the wrong environment. I know! A facepalm moment. I decided to look for a way to change the colors of a browser tab or a website based on the URL I visited. This is what I found.&lt;/p&gt;

&lt;h2 id=&quot;coloring-a-website-per-url&quot;&gt;Coloring a website per URL&lt;/h2&gt;

&lt;p&gt;After a quick search, I found the &lt;a href=&quot;https://github.com/fej-snikduj/URLColors&quot;&gt;URLColors extension&lt;/a&gt; in GitHub. It adds an opaque rectangle on top of a website. We only need to configure a keyword for the URL and a hex color. Optionally, it can make the rectangle blink.&lt;/p&gt;

&lt;p&gt;For example, this is how I colored Hacker News,&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// &amp;lt;site&amp;gt;, &amp;lt;color&amp;gt;, &amp;lt;flash|no&amp;gt;, &amp;lt;timerInSeconds&amp;gt;, &amp;lt;border-width&amp;gt;, &amp;lt;opacity&amp;gt;
news.ycombinator.com, #b58900, no, 0, 10px, 0.5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used this extension to color the OpenSearch dashboard and other websites I work with. I use the &lt;a href=&quot;https://ethanschoonover.com/solarized/&quot;&gt;Solarized theme&lt;/a&gt; and different color temperatures and rectangle width per environment.&lt;/p&gt;

&lt;p&gt;This is what an OpenSearch dashboard looks like,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-11-27-ColorsAsVisualAid/OpenSearchDashboard.png&quot; alt=&quot;An OpenSearch dashboard with a red rectangle around it&quot; width=&quot;400px&quot; /&gt;
    &lt;figcaption&gt;An OpenSearch dashboard for a non-development environment&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I go with a red and thick rectangle that blinks for Production-related environments.&lt;/p&gt;

&lt;h2 id=&quot;coloring-management-studio-bar-per-connection-string&quot;&gt;Coloring Management Studio bar per connection string&lt;/h2&gt;

&lt;p&gt;I use a similar trick with SQL Server Management Studio. When connecting to a new server, under the “Options” button, we can change the color of the status bar,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-11-27-ColorsAsVisualAid/SqlServerManagementStudio.png&quot; alt=&quot;SQL Server Management Studio &apos;Use custom color&apos; option&quot; width=&quot;400px&quot; /&gt;
    &lt;figcaption&gt;SQL Server Management Studio &apos;Use custom color&apos; option&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Voilà! No more changes in the Production environment by mistake. No more time wasted looking at the wrong website. Colors are helpful for that.&lt;/p&gt;

&lt;p&gt;Even we can change Visual Studio title bar color with the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Wumpf.SolutionColor&quot;&gt;SolutionColor extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more productivity tricks, check &lt;a href=&quot;/2023/11/13/DeclutteringUBlockOrigin/&quot;&gt;How to declutter sites with uBlock Origin filters&lt;/a&gt; and &lt;a href=&quot;/2023/09/18/FormatSqlFilesOnCommit/&quot;&gt;how to automatically format SQL files with Git&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to declutter sites with uBlock Origin filters</title>
   <link href="https://canro91.github.io/2023/11/13/DeclutteringUBlockOrigin/"/>
   <updated>2023-11-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/11/13/DeclutteringUBlockOrigin</id>
   <content type="html">&lt;p&gt;These days while procastinating on HackerNews, I found &lt;a href=&quot;https://news.ycombinator.com/item?id=37584134&quot;&gt;this submission&lt;/a&gt;. It points to a &lt;a href=&quot;https://github.com/mig4ng/ublock-origin-filters&quot;&gt;GitHub repo&lt;/a&gt; with some uBlock Origin filters to clean up websites.&lt;/p&gt;

&lt;p&gt;I learned that I not only can block elements in a page with uBlock Origin, but also restyle them. Ding, ding, ding! These are the uBlock Origin filters I’m using to declutter some site I visit often.&lt;/p&gt;

&lt;h2 id=&quot;1-ublock-origin-filters-to-restyle-elements&quot;&gt;1. uBlock Origin filters to restyle elements&lt;/h2&gt;

&lt;p&gt;A uBlock Origin filter to restyle an element looks like this,&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;domain&amp;gt;##&amp;lt;selector&amp;gt;:style(&amp;lt;new-css-here&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here are the filters I used to restyle HackYourNews and HackerNews,&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hackyournews.com##body:style(width: 960px; margin: 0 auto;)
hackyournews.com##.title:style(font-size: 18pt !important;)
hackyournews.com##.ratings:style(font-size: 12pt !important;)
hackyournews.com##.subtext:style(font-size: 14pt !important;)

news.ycombinator.com###hnmain:style(background-color: #fdf6e3; width: 960px !important; margin: 0 auto !important;)
news.ycombinator.com##.rank:style(font-size: 14pt !important;)
news.ycombinator.com##.titleline:style(font-size: 16pt !important;)
news.ycombinator.com##.sitebit.comhead:style(font-size: 12pt !important;)
news.ycombinator.com##.subtext:style(font-size: 12pt !important;)
news.ycombinator.com##.spacer:style(height: 12px !important;)
news.ycombinator.com##.toptext:style(font-size: 12pt !important;)
news.ycombinator.com##.comment:style(font-size: 14pt !important;)
news.ycombinator.com##span.comhead:style(font-size: 12pt !important;)
news.ycombinator.com##.morelink:style(font-size: 14pt !important;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-how-to-install-custom-ublock-origin-filters-in-brave&quot;&gt;2. How to install custom uBlock Origin filters in Brave&lt;/h2&gt;

&lt;p&gt;To install these filters in Brave, let’s navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brave://settings/shields/filters&lt;/code&gt;, paste the filters, and hit “Save.”&lt;/p&gt;

&lt;p&gt;This is how HackerNews looked without my filters,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-11-13-DeclutteringUBlockOrigin/Before.png&quot; alt=&quot;HackerNew front page&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;HackerNews front page without any restyling&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And this is how it looks after restyling it,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-11-13-DeclutteringUBlockOrigin/After.png&quot; alt=&quot;HackerNew front page after restyling&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;HackerNews front page with some uBlock Origin filters&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I reduced the page width and increased font size for more readability.&lt;/p&gt;

&lt;p&gt;For other sites, I install these extensions: &lt;a href=&quot;https://www.modernwiki.app/&quot;&gt;Modern Wiki&lt;/a&gt; to restyle Wikipedia, &lt;a href=&quot;https://github.com/elrumo/stackOverflow_focus&quot;&gt;StackOverflow Focus&lt;/a&gt;, and &lt;a href=&quot;https://chromewebstore.google.com/detail/df-tube-distraction-free/mjdepdfccjgcndkmemponafgioodelna&quot;&gt;Distraction-Free YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to use uBlock Origin filters to declutter websites. I like clean and minimalistic designs. Before learning about uBlock Origin filters, I started to dabble into browser extension development to restyle sites. With these filters, it’s easier.&lt;/p&gt;

&lt;p&gt;What site would you like to declutter with this trick?&lt;/p&gt;

&lt;p&gt;For more productivity content, check &lt;a href=&quot;/2022/12/10/ReplaceKeywordInFile/&quot;&gt;how to replace keywords in file with Bash&lt;/a&gt;, &lt;a href=&quot;/2022/12/09/RenameProjectsVisualStudio/&quot;&gt;how to rename C# project files with Git&lt;/a&gt;, and &lt;a href=&quot;/2023/09/18/FormatSqlFilesOnCommit/&quot;&gt;how to format SQL files with Git and Poor Man’s T-SQL Formatter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Five lessons while working with OrmLite</title>
   <link href="https://canro91.github.io/2023/10/30/LessonsOnOrmLite/"/>
   <updated>2023-10-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/10/30/LessonsOnOrmLite</id>
   <content type="html">&lt;p&gt;Back in the day, for my &lt;a href=&quot;/AdventOfCode2022&quot;&gt;Advent of Posts&lt;/a&gt; I shared some &lt;a href=&quot;/2022/12/13/LessonsOnHangfireAndOrmLite/&quot;&gt;lessons on Hangfire and OrmLite&lt;/a&gt;. In this year, for one of my client’s project I’ve been working with OrmLite a lot. Let me expand on those initial lessons and share some others.&lt;/p&gt;

&lt;h2 id=&quot;1-ignoreonupdate-attribute&quot;&gt;1. IgnoreOnUpdate attribute&lt;/h2&gt;

&lt;p&gt;When using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt; or any update method, OrmLite omits properties marked with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnUpdate]&lt;/code&gt; attribute in the generated SQL statement. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs#L1013&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreOnUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrmLiteDoesNotUpdateThisOne&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used this attribute when &lt;a href=&quot;/2022/12/11/AuditFieldsWithOrmLite/&quot;&gt;inserting and updating audit fields&lt;/a&gt; to avoid messing with creation dates when updating records.&lt;/p&gt;

&lt;p&gt;Also OrmLite has similar attributes for insertions and queries: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnInsertAttribute]&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnSelectAttribute]&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-queryfirst-vs-sqlscalar&quot;&gt;2. QueryFirst vs SqlScalar&lt;/h2&gt;

&lt;p&gt;OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QueryFirst()&lt;/code&gt; method requires an explicit transaction as a parameter. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/Dapper/SqlMapper.cs#L738&quot;&gt;Source&lt;/a&gt; Unlike &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QueryFirst()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlScalar()&lt;/code&gt; uses the same transaction from the input database connection. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/OrmLiteReadApi.cs#L524&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I learned this because I had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoesIndexExist()&lt;/code&gt; method inside a &lt;a href=&quot;/2020/08/15/Simple.Migrations/&quot;&gt;database migration&lt;/a&gt; and it failed with the message &lt;em&gt;“ExecuteReader requires the command to have a transaction…“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is what I had to change,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoesIndexExist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDbConnection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doesIndexExistSql&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;
&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;;
&lt;/span&gt;    
    &lt;span class=&quot;c1&quot;&gt;// Before&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// return connection.QueryFirst&amp;lt;bool&amp;gt;(isIndexExistsSql);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Exception: ExecuteReader requires the command to have a transaction...&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// After&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SqlScalar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doesIndexExistSql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                      ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-create-indexes&quot;&gt;3. Create Indexes&lt;/h2&gt;

&lt;p&gt;Apart from reading and writing records, OrmLite can modify the database schema, for example to create tables and indexes.&lt;/p&gt;

&lt;p&gt;To create an index for a table, we could either annotate fields or classes. For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CompositeIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldsNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ReleaseYear&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AnOptionalIndexName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, OrmLite has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateIndex()&lt;/code&gt; method that receives an expression, like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateIndex()&lt;/code&gt; creates indexes with names like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idx_TableName_FieldName&lt;/code&gt;. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs#L1701&quot;&gt;Source&lt;/a&gt; We can omit the index name if we’re fine with this naming convention.&lt;/p&gt;

&lt;h2 id=&quot;4-tag-queries-to-easy-troubleshooting&quot;&gt;4. Tag queries to easy troubleshooting&lt;/h2&gt;

&lt;p&gt;To identify the source of queries, OrmLite has two methods: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TagWith()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TagWithCallSite()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// Some filters here...&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TagWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AnAwesomeQuery&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// Or&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;//.TagWithCallSite();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TagWith()&lt;/code&gt;, OrmLite includes a comment at the top of the generated SQL query with the identifier we pass.&lt;/p&gt;

&lt;p&gt;For the previous tagged query, this is the generated SQL statement,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;-- AnAwesomeQuery&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;ReleaseYear&quot;&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Movie&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TagWithCallSite()&lt;/code&gt;, Ormlite uses the path and line number of the file that made that database call instead.&lt;/p&gt;

&lt;p&gt;This is a similar trick to the one we use to &lt;a href=&quot;/2020/12/03/DebugDynamicSQL/&quot;&gt;debug dynamic SQL queries&lt;/a&gt;. It helps up to traceback queries once we found them in our database plan cache.&lt;/p&gt;

&lt;h2 id=&quot;5-loadselectasync-and-unparameterized-queries&quot;&gt;5. LoadSelectAsync and unparameterized queries&lt;/h2&gt;

&lt;p&gt;OrmLite has two convenient methods: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelect()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelectAsync()&lt;/code&gt;. They find some records and load their child references.&lt;/p&gt;

&lt;p&gt;Let’s write the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt; classes,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Director&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;References&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now let’s use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelectAsync()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// Some filters here&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadSelectAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                             ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// It loads movies and their child directors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelect()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelectAsync()&lt;/code&gt;, OrmLite doesn’t parameterize the internal query used to load the child entities. Arrrggg!&lt;/p&gt;

&lt;p&gt;I’m not sure if it’s a bug or a feature. But, to load child entities, OrmLite “inlines” the parameters used to run the parent query. We will see in the plan cache of our database lots of unparameterized queries.&lt;/p&gt;

&lt;p&gt;See it by yourself in OrmLite source code, &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/Async/OrmLiteReadCommandExtensionsAsync.cs#L409&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/Support/LoadList.cs#L48&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After finding out about this behavior, I ended up ditching &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelectAsync()&lt;/code&gt; and using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SelectAsync()&lt;/code&gt; instead, like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moviesQuery&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;// Some filters here&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;moivesQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directorsQuery&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;In&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moviesQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directorsQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Probably there’s a better solution, but that was my workaround to avoid a flooded plan cache. I could afford an extra roundtrip to the database and I didn’t want to write SQL queries by hand. C’mon!&lt;/p&gt;

&lt;p&gt;Voilà! These are some of the lessons I’ve learned while working with OrmLite. Again, things we only find out when we adventure to read our libraries source code.&lt;/p&gt;

&lt;p&gt;To read more content on OrmLite, check &lt;a href=&quot;/2023/06/26/PassDataTableOrmLite/&quot;&gt;how to pass a DataTable as parameter to an OrmLite query&lt;/a&gt; and &lt;a href=&quot;/2023/10/16/SubqueriesAndOrmLite/&quot;&gt;how to join to a subquery with OrmLite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to join to subqueries with OrmLite</title>
   <link href="https://canro91.github.io/2023/10/16/SubqueriesAndOrmLite/"/>
   <updated>2023-10-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/10/16/SubqueriesAndOrmLite</id>
   <content type="html">&lt;p&gt;Another day working with OrmLite. This time, I needed to support a report page with a list of dynamic filters and sorting fields. Instead of writing a plain SQL query, I needed to write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlExpression&lt;/code&gt; that joins to a subquery. OrmLite doesn’t support that. This is what I learned (or hacked) today.&lt;/p&gt;

&lt;p&gt;Let’s imagine we need to write an SQL query for a report to show all directors based on filters like name, birthdate, and other conditions. Next to each director, we need to show their movie count and other counts. For me, it was reservations and rooms. But the idea is still the same.&lt;/p&gt;

&lt;h2 id=&quot;1-using-a-sql-query-with-a-cte&quot;&gt;1. Using a SQL query with a CTE&lt;/h2&gt;

&lt;p&gt;Since we needed to support filters based on the user’s input, the best solution would be to write a &lt;a href=&quot;/2021/03/08/HowNotToWriteDynamicSQL/&quot;&gt;dynamic SQL query&lt;/a&gt;. I know, I know! That’s tedious.&lt;/p&gt;

&lt;p&gt;If we have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; tables, we could write a query like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieCount&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt;
	   &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Count&lt;/span&gt;
	   &lt;span class=&quot;cm&quot;&gt;/* More aggregations here */&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
	&lt;span class=&quot;cm&quot;&gt;/* Some filters here */&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Count&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieCount&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Country&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;USA&apos;&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* More filters here */&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* Sorting by other filters here */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m using a common table expression, CTE. I have already used them to &lt;a href=&quot;/2022/03/07/OptimizeGroupBySQLServer/&quot;&gt;optimize queries with ORDER BY&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this example, a simple JOIN without any CTE would work. But let me prove a point and finish this post.&lt;/p&gt;

&lt;h2 id=&quot;2-using-ormlite-sqlexpression&quot;&gt;2. Using OrmLite SqlExpression&lt;/h2&gt;

&lt;p&gt;Let’s use these &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; classes to represent the one-to-many relationship between directors and their movies,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Director&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Country&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;References&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While trying to translate that query to OrmLite expressions, I realized OrmLite doesn’t support joining to subqueries. Arrrggg!&lt;/p&gt;

&lt;p&gt;I rolled up my sleeves and started to take a deeper look.&lt;/p&gt;

&lt;p&gt;I ended up hacking this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceStack.DataAnnotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceStack.OrmLite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;JoiningToSubqueries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JoinTetsts&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ItWorksItWorks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;...Any SQL Server connection string here...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrmLiteConnectionFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 0. Create Movie and Director tables&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 1. Populate some data&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jamesCameron&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;James Cameron&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Country&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Canada&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jamesCameron&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stevenSpielberg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Steven Spielberg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Country&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;USA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Raiders of the Lost Ark&quot;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Jurassic Park&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stevenSpielberg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;georgeLucas&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;George Lucas&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Country&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;USA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Star Wars: A New Hope&quot;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;georgeLucas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 2. Write a subquery to do the counting&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieCountPerDirector&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// We could add some filters here...&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GroupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Custom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;COUNT(*)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 2. Write the parent query to filter and sort&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LeftJoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movieCountPerDirector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subQueryAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;mc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// It receives a subquery, join expression&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// and alias&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We could add some filters here...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Country&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;USA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;MovieCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Custom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mc.Count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//                      ^^^^&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// Same alias as subQueryAlias parameter&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We could change the sorting column here too...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mc.Count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SelectAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorAndMovieCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Steven Spielberg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;directors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;George Lucas&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DirectorAndMovieCount&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Country&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After creating the two tables and adding some movies, we wrote the aggregation part inside the CTE with a normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlExpression&lt;/code&gt;. That’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movieCountPerDirector&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Then, we needed the JOIN between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movieCountPerDirector&lt;/code&gt; and the parent query to apply all the filters and sorting. We wrote,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LeftJoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movieCountPerDirector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirectorId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;subQueryAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;mc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...    &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LeftJoin()&lt;/code&gt; that received a subquery, a joining expression, and an alias.&lt;/p&gt;

&lt;p&gt;We might use aliases on the tables to avoid name clashes on the JOIN expression.&lt;/p&gt;

&lt;h2 id=&quot;3-leftjoin-with-another-sqlexpression&quot;&gt;3. LeftJoin with another SqlExpression&lt;/h2&gt;

&lt;p&gt;And this is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LeftJoin()&lt;/code&gt; method,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlExpressionExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LeftJoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSubquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SqlExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSubquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSubquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;joinExpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subqueryAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This is to &quot;move&quot; parameters from the subquery&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// to the parent query while keeping the right&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// parameter count and order.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Otherwise, we could have a parameter named &apos;@0&apos;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// on the parent and subquery that refer to&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// different columns and values.&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subqueryParams&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subquerySql&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FormatFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToSelectStatement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filterParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subqueryParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// This is a hacky way of replacing the original&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// table name from the join condition with the&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// subquery alias&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// From:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//      &quot;table1&quot;.&quot;Id&quot; = &quot;table2&quot;.&quot;Table1Id&quot;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// To:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//      &quot;table1&quot;.&quot;Id&quot; = &quot;mySubqueryAlias&quot;.&quot;Table1Id&quot;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCondition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;joinExpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;definition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ModelDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSubquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Definition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aliasCondition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;definition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCondition&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCondition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;definition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subqueryAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// For example,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// LEFT JOIN (SELECT Column1 FROM ...) cte ON parent.Id = cte.parentId&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CustomJoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSubquery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;LEFT JOIN (&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subquerySql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subqueryAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ON &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aliasCondition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FormatFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SqlExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filterParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filterParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pLiteral&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;{&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filterParam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filterParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filterParam&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlInValues&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqlParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlIn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateInParamSql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pLiteral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pLiteral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlInValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmptyIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filterParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pLiteral&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ParameterName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateInParamSql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SqlExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sbParams&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilderCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Allocate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sbParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sbParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;sbParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ParameterName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlIn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringBuilderCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnAndFree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sbParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s go through it!&lt;/p&gt;

&lt;p&gt;It starts by copying the parameters from the subquery into the parent query. Otherwise, we could end up with parameters with the same name that refer to different values.&lt;/p&gt;

&lt;p&gt;OrmLite names parameters using numbers, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@0&lt;/code&gt;. On the subquery, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@0&lt;/code&gt; could refer to another column as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@0&lt;/code&gt; on the parent query.&lt;/p&gt;

&lt;p&gt;Then, it converts the joining expression into a SQL string. We used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Visit()&lt;/code&gt; method for that. Then, if the subquery has an alias, it replaces the table name with that alias on the generated SQL fragment for the join expression. And it builds the final raw SQL and calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomJoin()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I brought the &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/Expressions/SqlExpression.cs#L557&quot;&gt;FormatFilter()&lt;/a&gt; and &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/main/ServiceStack.OrmLite/src/ServiceStack.OrmLite/Expressions/SqlExpression.cs#L588&quot;&gt;CreateInParamSql()&lt;/a&gt; methods from OrmLite source code. They’re private on the OrmLite source code.&lt;/p&gt;

&lt;p&gt;Voilà! That is what I learned (or hacked) today. Again, things we learn when we read the source code of our libraries. We used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Visit()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomJoin()&lt;/code&gt;, and two helper methods we brought from the OrmLite source code to make this work.&lt;/p&gt;

&lt;p&gt;We only used LEFT JOIN, but we can extend this idea to support INNER JOIN.&lt;/p&gt;

&lt;p&gt;As an alternative to this &lt;del&gt;hacky&lt;/del&gt; solution, we could write a dynamic SQL query. Next idea! Or we could create an indexed view to replace that counting subquery with a normal join. We could roll a custom method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JoinToView()&lt;/code&gt; to append a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH NO_EXPAND&lt;/code&gt; to the actual JOIN. I know everybody can’t afford a SQL Server Enterprise edition.&lt;/p&gt;

&lt;p&gt;For more OrmLite content, check &lt;a href=&quot;/2022/12/11/AuditFieldsWithOrmLite/&quot;&gt;how to automatically insert and update audit fields with OrmLite&lt;/a&gt;, &lt;a href=&quot;/2023/06/26/PassDataTableOrmLite/&quot;&gt;how to pass a DataTable as a parameter to a SqlExpression&lt;/a&gt; and &lt;a href=&quot;/2022/12/13/LessonsOnHangfireAndOrmLite/&quot;&gt;some lessons I learned after working with OrmLite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Monday Links: CQRS, Negotiating, and Project Managers</title>
   <link href="https://canro91.github.io/2023/10/02/MondayLinks/"/>
   <updated>2023-10-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/10/02/MondayLinks</id>
   <content type="html">&lt;p&gt;One conference and four articles I found interesting in the last month.&lt;/p&gt;

&lt;h2 id=&quot;cqrs-pitfalls-and-patterns---udi-dahan&quot;&gt;CQRS pitfalls and patterns - Udi Dahan&lt;/h2&gt;

&lt;p&gt;I share the points from this presentation about overarchitecting simple applications when there isn’t a compelying reason for that. I’ve witnessed developers and managers using and promoting Domain Driven Design as the golden hammer to write all aplications, even simple CRUD-like ones. The speaker shares that eventually-consistent CQRS is not for that type of applications.&lt;/p&gt;

&lt;p&gt;Althought this talk might seem about coding, it’s more about bussiness and designing software.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/Lw04HRF8ies?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;trying-to-become-a-better-developer-by-learning-more-about-aviation&quot;&gt;Trying to become a better developer by learning more about aviation&lt;/h2&gt;

&lt;p&gt;I have always been intrigued by planes. We started from the Wright brothers to planes flying by themselves. I have always been interested by software, procedures, and processes that keep planes in the air.&lt;/p&gt;

&lt;p&gt;We, as software engineers, have a lot to learn from pilots. For example, “Aviate, Navigate, Communicate” is one of the principles pilots follow during incidents. Their number one priority is keeping the plane flying. We could take that principle to the software engineering world.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/@Fcmam5/trying-to-become-a-better-developer-by-learning-more-about-aviation-5241e7092f7e&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-sabotage-your-salary-negotiations-efforts-before-you-even-start&quot;&gt;How to sabotage your salary negotiations efforts before you even start&lt;/h2&gt;

&lt;p&gt;Interviewing is another skill to master by itself. One thing I’ve found everywhere online is never say a number first. This article shows common mistakes and sample scripts when negotiating salaries.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://interviewing.io/blog/sabotage-salary-negotiation-before-even-start&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;lessons-learned-as-a-software-developer-turned-project-manager&quot;&gt;Lessons learned as a software developer turned project manager&lt;/h2&gt;

&lt;p&gt;I really liked this one! I’ve been in teams where project managers have no idea about building software and they only focus on running meetings and other “ceremonies.” I wish all project managers in software companies had a development background. I know I’m asking for the moon.&lt;/p&gt;

&lt;p&gt;Some quotes from this article:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;“the most difficult challenge in a technical project is the communication between parties”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“the scope of the project and what needs to be done should be clear to everyone”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“don’t ever, ever, call your colleagues or developers resources or FTE’s (full time equivalent), they’re humans, not beans”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://karimjedda.com/lessons-learned-developer-to-project-manager/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-opt-out-of-the-career-ladder&quot;&gt;How To Opt Out Of The Career Ladder&lt;/h2&gt;

&lt;p&gt;If you don’t believe in the career ladder, this one is for you. It contains a questionnaire to help taking the first steps into a different path. &lt;em&gt;“being clear with yourself on what role you actually want work to play in your life is an important foundational step to exploring what’s next”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://devinterrupted.substack.com/p/how-to-opt-out-of-the-career-ladder&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links. Have you seen developers switching to project management too? Do you have any tips to negotiate salaries? Until next Monday Links.&lt;/p&gt;

&lt;p&gt;In the meantime, don’t miss the previous &lt;a href=&quot;/2023/07/10/MondayLinks/&quot;&gt;Monday Links on NDC Conference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to automatically format SQL files with Git and Poor Man&apos;s T-SQL Formatter</title>
   <link href="https://canro91.github.io/2023/09/18/FormatSqlFilesOnCommit/"/>
   <updated>2023-09-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/09/18/FormatSqlFilesOnCommit</id>
   <content type="html">&lt;p&gt;I believe we shouldn’t discuss formatting and linting during code reviews. That should be automated. With that in mind, these days, I learned how to automatically format SQL files with Git and Poor Man’s T-SQL Formatter for one of my client’s projects.&lt;/p&gt;

&lt;p&gt;I already shared about &lt;a href=&quot;/2020/09/30/FormatSQL/&quot;&gt;two free tools to format SQL files&lt;/a&gt;. Poor Man’s T-SQL Formatter is one of the two. It’s free and open source.&lt;/p&gt;

&lt;h2 id=&quot;1-format-sql-files-on-commits&quot;&gt;1. Format SQL files on commits&lt;/h2&gt;

&lt;p&gt;I wanted to format my SQL files as part of my development workflow. I thought about a pre-commit Git hook for that. I was already familiar with Git hooks since I use one to &lt;a href=&quot;/2020/09/02/TwoRecurringReviewComments/&quot;&gt;put task numbers from branch names into commit messages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After searching online, I found a Bash script to list all created, modified, and renamed files before committing them. I used &lt;a href=&quot;https://www.phind.com/&quot;&gt;Phind&lt;/a&gt;, “the AI search engine for developers.” These are the query I used:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“How to create a git commit hook that lists all files with .sql extension?” and as a follow-up,&lt;/li&gt;
  &lt;li&gt;“What are all possible options for the parameter –diff-filter on the git diff command?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, I found out that Poor Man’s T-SQL Formatter is available as a &lt;a href=&quot;https://github.com/TaoK/poor-mans-t-sql-formatter-npm-cli&quot;&gt;Node.js command utility&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using these two pieces, this is the pre-commit file I came up with,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;git diff &lt;span class=&quot;nt&quot;&gt;--cached&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name-only&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--diff-filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ACMR&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$files&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0

&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;file &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
    if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.sql &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Formatting: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;# 1. Prettify it&lt;/span&gt;
        sqlformat &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--breakJoinOnSections&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-trailingCommas&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--spaceAfterExpandedComma&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;# 2. Add it back to the staging area&lt;/span&gt;
        git add &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi
done

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used these three options: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--breakJoinOnSections&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-trailingCommas&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--spaceAfterExpandedComma&lt;/code&gt; to place ONs after JOINs and commas on a new line.&lt;/p&gt;

&lt;h2 id=&quot;2-test-the-pre-commit-hook&quot;&gt;2. Test the pre-commit hook&lt;/h2&gt;

&lt;p&gt;To test this Git hook, I created an empty repository, saved the above Bash script into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pre-commit&lt;/code&gt; file inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git/hooks&lt;/code&gt; folder, and installed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poor-mans-t-sql-formatter-cli&lt;/code&gt; package version 1.6.10.&lt;/p&gt;

&lt;p&gt;For the actual SQL file, I used the query to find StackOverflow posts with many “thank you” answers, &lt;a href=&quot;https://data.stackexchange.com/stackoverflow/query/886/posts-with-many-thank-you-answers&quot;&gt;Source&lt;/a&gt;,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParentId&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posttypeid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;like&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%hank%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentid&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;having&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is where all the magic happened when committing the previous SQL file,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-09-18-FormatSqlFilesOnCommit/Console.png&quot; alt=&quot;Sequence of Git commands to commit a file&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Committing a ThankYou.sql file and seeing the magic happening&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;By the way, I use &lt;a href=&quot;/2020/04/13/ProgramThatSave100Hours/&quot;&gt;some Git alias&lt;/a&gt; as part of my development workflow.&lt;/p&gt;

&lt;p&gt;And this is the formatted SQL file,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParentId&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posttypeid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%hank%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentid&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;HAVING&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how to format SQL files automatically with Git. The command line version of Poor Man’s T-SQL Formatter is not that fast. But it’s still faster than copying a SQL file, firing a browser with an online linter, formatting it, and pasting it back.&lt;/p&gt;

&lt;p&gt;Poor Man’s T-SQL Formatter might not be perfect, but with a simple change in our script, we can bring any other SQL formatter we can call from the command line.&lt;/p&gt;

&lt;p&gt;After this trick, I don’t want to leave or read another comment like “please format this file” during code review.&lt;/p&gt;

&lt;p&gt;For more content, check &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;my guide to Code Reviews&lt;/a&gt;, &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;my Visual Studio setup for C#&lt;/a&gt;, and the &lt;a href=&quot;/2022/12/19/LessonsAsReviewer/&quot;&gt;lessons I’ve learned as a code reviewer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A business case against massive unrequested refactorings</title>
   <link href="https://canro91.github.io/2023/09/04/AgainstMassiveUnrequestedRefactorings/"/>
   <updated>2023-09-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/09/04/AgainstMassiveUnrequestedRefactorings</id>
   <content type="html">&lt;p&gt;Blindly following coding principles is a bad idea.&lt;/p&gt;

&lt;p&gt;“Leave the basecamp cleaner,” “Make the change easy then make the easy change”…&lt;/p&gt;

&lt;p&gt;Often, we follow those two principles and start huge refactoring sessions with good intentions but without considering the potential consequences.&lt;/p&gt;

&lt;p&gt;Let me share two stories of refactoring sessions that led to unintended consequences and the lesson behind them.&lt;/p&gt;

&lt;h2 id=&quot;changing-entities-and-value-objects&quot;&gt;Changing Entities and Value Objects&lt;/h2&gt;

&lt;p&gt;At a past job, a team member decided to refactor the entire solution before working on his task.&lt;/p&gt;

&lt;p&gt;He changed every Domain Entity, &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;Value Object&lt;/a&gt;, and database table. What he found wasn’t “scalable” in his experience.&lt;/p&gt;

&lt;p&gt;The project was still in its early stage and the rest of the team was waiting for his task.&lt;/p&gt;

&lt;p&gt;One week later, we were still discussing about names, &lt;a href=&quot;/2022/12/15/CreateProjectStructureWithDotNetCli/&quot;&gt;folder structure&lt;/a&gt;, and the need for that refactoring in the first place.&lt;/p&gt;

&lt;p&gt;We all were blocked waiting for him to finish the mess he had created.&lt;/p&gt;

&lt;h2 id=&quot;changing-class-and-table-names&quot;&gt;Changing Class and Table Names&lt;/h2&gt;

&lt;p&gt;At another job, our team’s architect decided to work over the weekend.&lt;/p&gt;

&lt;p&gt;And the next thing we knew next Monday morning was that almost all class and table names had been changed. The architect decided to rename everything. He simply didn’t like the initial naming conventions. Arrrggg!&lt;/p&gt;

&lt;p&gt;We found an email in our inboxes listing the things he had broken along the way.&lt;/p&gt;

&lt;p&gt;We spent weeks migrating user data from the old database schema to the new one.&lt;/p&gt;

&lt;p&gt;These are two examples of refactoring sessions that went sideways. Nobody asked those guys to change anything in the first place.&lt;/p&gt;

&lt;p&gt;Even there was no need or business case for that in the first place.&lt;/p&gt;

&lt;p&gt;I have a term for these refactoring sessions: &lt;strong&gt;massive unrequested refactoring.&lt;/strong&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1634586648651-f1fb9ec10d90?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4NDg3OTg0NA&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;A room with some tools in it&quot; /&gt;

&lt;figcaption&gt;Another massive but unfinished refactoring...Photo by &lt;a href=&quot;https://unsplash.com/@st_lehner?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Stefan Lehner&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/biRt6RXejuk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;the-need-for-refactoring&quot;&gt;The Need for Refactoring&lt;/h2&gt;

&lt;p&gt;I’m not saying we shouldn’t refactor our code.&lt;/p&gt;

&lt;p&gt;I believe in the “leave the basecamp cleaner than the way you found it” mantra.&lt;/p&gt;

&lt;p&gt;But, before embarking on a massive refactoring, let’s ask ourselves if it’s truly necessary and if the team can afford it, not only in terms of money but also time and dependencies.&lt;/p&gt;

&lt;p&gt;Often, we get too focused on &lt;a href=&quot;/2022/12/07/BanningSomeNamingConventions/&quot;&gt;naming variables, functions, and classes&lt;/a&gt; to see the bigger picture and the overall project in perspective.&lt;/p&gt;

&lt;p&gt;“Perfect is the enemy of good.”&lt;/p&gt;

&lt;p&gt;And if there isn’t a viable alternative, let’s split that massive refactoring into &lt;a href=&quot;/2022/12/19/LessonsAsReviewer/&quot;&gt;separate, focused, and short Pull Requests&lt;/a&gt; that can be reviewed in a single review session without much back and forth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best refactorings are the small ones that slowly and incrementally improve the health of the overall project. One step at a time. Not the massive unrequested ones.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Voilà! That’s my take on massive unrequested refactorings. Have you ever done one too? What impact did it have? Did it turn out well? Remember, all code we write should move the project closer to its finish line. Often, massive unrequested refactorings don’t do that.&lt;/p&gt;

&lt;p&gt;In my two stories, those refactoring sessions ended up blocking people and creating more work.&lt;/p&gt;

&lt;p&gt;These refactorings remind me of the analogy that &lt;a href=&quot;/2021/01/25/LivableCode/&quot;&gt;coding is like living in a house&lt;/a&gt;. A massive unrequested refactoring would be like a full home renovation while staying there!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>There&apos;s No Such Thing as Job Security: Three Lessons on Layoffs</title>
   <link href="https://canro91.github.io/2023/08/21/OnLayoffs/"/>
   <updated>2023-08-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/08/21/OnLayoffs</id>
   <content type="html">&lt;p&gt;I’ve been laid off more than once.&lt;/p&gt;

&lt;p&gt;I know how it feels. I know that momentary feeling of relief followed by the uncertainty of a “What am I going to do now?”&lt;/p&gt;

&lt;p&gt;If you haven’t been living under a rock, I bet you have heard the news about layoffs in the tech industry.&lt;/p&gt;

&lt;p&gt;They’re so common these days that there’s even a page to report and track companies laying off their people: &lt;a href=&quot;https://layoffs.fyi/&quot;&gt;layoffs.fyi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some days ago, I got a message from a close friend who was laid off. These are three the lessons I’ve learned on layoffs I shared with her.&lt;/p&gt;

&lt;h2 id=&quot;1-job-security-is-an-illusion&quot;&gt;1. Job security is an illusion&lt;/h2&gt;

&lt;p&gt;I don’t know who makes us believe there’s such a thing as “job security.” That’s an illusion.&lt;/p&gt;

&lt;p&gt;In my early days at college, I thought the safest route was being an employee. I was so wrong! I only needed being laid off once to change my mind.&lt;/p&gt;

&lt;p&gt;We could lose our jobs anytime for reasons we don’t and can’t control. A pandemic, a company going bankrupt, or a recession.&lt;/p&gt;

&lt;p&gt;Based on &lt;a href=&quot;https://layoffs.fyi/&quot;&gt;layoffs.fyi&lt;/a&gt;, the site that tracks layoffs I just told you,&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;165,269 US tech employees lost their job in 2022,&lt;/li&gt;
  &lt;li&gt;263,180 in 2023, and&lt;/li&gt;
  &lt;li&gt;89,193 in 2024 until May.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real question is when it will happen, not if it will ever happen to us. We’re better off preparing for that.&lt;/p&gt;

&lt;h2 id=&quot;2-have-an-emergency-fund&quot;&gt;2. Have an emergency fund&lt;/h2&gt;

&lt;p&gt;I can’t stress this enough. This one of the things &lt;a href=&quot;/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer/&quot;&gt;I wished I had learned earlier&lt;/a&gt;: have an emergency fund.&lt;/p&gt;

&lt;p&gt;An emergency fund is enough savings to cover our essential expenses for some time. The longer, the better.&lt;/p&gt;

&lt;p&gt;That’s the breathing room until you figure out something.&lt;/p&gt;

&lt;p&gt;And it’s the difference between being picky about the next job or accepting anything to pay the bills.&lt;/p&gt;

&lt;h2 id=&quot;3-always-be-ready&quot;&gt;3. Always be ready&lt;/h2&gt;

&lt;p&gt;Let’s always have our CVs updated. Stay in touch with our colleagues and ex-coworkers. Build our professional network.&lt;/p&gt;

&lt;p&gt;Let’s always be ready for an interview. Have our data structures and “tell me about yourself” muscles in shape.&lt;/p&gt;

&lt;p&gt;Interviewing is broken, I know! But let’s always be ready to leave.&lt;/p&gt;

&lt;p&gt;Don’t wait for a layoff to establish an online presence and grow your network. By then, it will be too late.&lt;/p&gt;

&lt;p&gt;Voilà! Those are my thoughts about layoffs. I learned that after losing a job, there’s always a positive change. That takes us out of our comfort zone. “Pastures are always greener on the other side,” I guess.&lt;/p&gt;

&lt;p&gt;For more career lessons, read these &lt;a href=&quot;/2019/08/19/FiveLessonsAfterFiveYears/&quot;&gt;five lessons I learned in my first five years as a software engineer&lt;/a&gt; and &lt;a href=&quot;/2020/08/08/LessonsOnRemoteWork/&quot;&gt;ten lessons learned after one year of remote work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Too many layers: My take on Queries and Layers</title>
   <link href="https://canro91.github.io/2023/08/07/TooManyLayers/"/>
   <updated>2023-08-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/08/07/TooManyLayers</id>
   <content type="html">&lt;p&gt;These days I reviewed a pull request in one of my client’s projects and shared a thought about reading database entities and layering. I believe that project took layering to the extreme. These are my thoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For read-only database-access queries, reduce the number of layers in an application to avoid excessive mapping between layers and unneeded artifacts.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;too-many-layers-i-guess&quot;&gt;Too many layers, I guess&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;pull request I reviewed&lt;/a&gt; added a couple of API endpoints to power a report-like screen. These two endpoints only returned data given a combination of parameters. Think of showing all movies released on a date range with 4 or 5 stars. It wasn’t exactly that, but let’s use that example to prove a point.&lt;/p&gt;

&lt;p&gt;That project had database entities, domain objects, results wrapping DTOs, and responses. To add a new read-only API endpoint, we would need a request object, query, query handler, and repository.&lt;/p&gt;

&lt;p&gt;Inside the repository, we would need to map database entities to domain entities and &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;value objects&lt;/a&gt;. Inside the query handler, we would need to return a result object containing a collection of DTOs. Another mapping. Inside the API endpoint, we would need to return a response object. Yet another mapping. I guess you see where I’m going.&lt;/p&gt;

&lt;p&gt;This is the call chain of methods I found in that project:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-08-07-TooManyLayers/TooManyLayers.png&quot; alt=&quot;Sequence diagram to read a list of movies&quot; /&gt;
    &lt;figcaption&gt;Three layers and even more mappings&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And these are all the files we would need to add a new API endpoint and its dependencies:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;|-Api/
|---src/
|-----Movies/
|-------MovieQueryApi.cs
|-------GetMoviesQueryResponse.cs
|-Application/
|---src/
|-----Movies/
|-------GetMoviesQuery.cs
|-------GetMoviesQueryHandler.cs
|-------GetMoviesQueryResult.cs
|-------MovieDto.cs
|-Domain/
|---src/
|-----Movies/
|-------Movie.cs
|-------Director.cs
|-------Genre.cs
|-Infrastructure.Contracts/
|---src/
|-----Movies/
|-------IMovieRepository.cs
|-Infrastructure.SqlServer/
|---src/
|-----Movies/
|-------MovieRepository.cs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Technically, the objects inside the Domain were already there. By the way, we can &lt;a href=&quot;/2022/12/15/CreateProjectStructureWithDotNetCli/&quot;&gt;create that folder structure with dotnet cli&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s layering to the extreme. All those artifacts and about three mapping methods between layers are waaay too much to only read unprocessed entities from a database. Arrrggg! Too much complexity. We’re only reading data, not loading domain objects to call methods on them.&lt;/p&gt;

&lt;p&gt;I believe &lt;strong&gt;simple things should be simple to achieve&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;query-services-a-simpler-alternative&quot;&gt;Query Services: A simpler alternative&lt;/h2&gt;

&lt;p&gt;As an alternative to those artifacts and mappings, I like to follow the idea from the book &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;Hands-on Domain-Driven Design with .NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For read-only queries, the HODDD book uses two models:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Query Models&lt;/strong&gt; for the request parameters, and&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Read Models&lt;/strong&gt; for the request responses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, it calls the underlying storage mechanism directly from the API layer. Well, that’s too much for my own taste. But I like the simplicity of the idea.&lt;/p&gt;

&lt;p&gt;I prefer to use &lt;strong&gt;Query Services&lt;/strong&gt;. They are query handlers that live in the Infrastructure or Persistence layer, call the underlying storage mechanism, and return a read model we pass directly to the API layer. This way, we only have two layers and no mappings between them. We declutter our project from those extra artifacts!&lt;/p&gt;

&lt;p&gt;I mean something like this,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-08-07-TooManyLayers/FewerLayers.png&quot; alt=&quot;Sequence diagram to read a list of movies&quot; /&gt;
    &lt;figcaption&gt;Two layers and zero mappings&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And something like this,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;|-Api/
|---src/
|-----Movies/
|-------MovieQueryApi.cs
|-Application/
|---src/
|-----Movies/
|-------GetMoviesQueryModel.cs
|-------MoviesReadModel.cs
|-Infrastructure.SqlServer/
|---src/
|-----Movies/
|-------GetMoviesQueryService.cs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We put the input and output models in the Application layer since we want the query service in the Infrastructure layer. Although, the HODDD book places the input and output models and data-access code directly in the API layer. Way simpler in any case!&lt;/p&gt;

&lt;p&gt;Voilà! That’s my take on read-only queries, layers, and Domain-Driven Design artifacts. I prefer to keep read-only database access simple and use query services to avoid queries, query handlers, repositories, and the mappings between them. What do you think? Do you also find all those layers and artifacts excessive?&lt;/p&gt;

&lt;p&gt;If you want to read more content on Domain-Driven Design, check &lt;a href=&quot;/2020/12/10/PrimitiveObsession/&quot;&gt;a case of primitive obsession&lt;/a&gt; and &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;my takeaways from the book Domain Modeling Made Functional&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>This Is How I&apos;d Start an Ultralearning Project To Become a Software Engineer</title>
   <link href="https://canro91.github.io/2023/07/24/AdviceToStartAnUltralearningProject/"/>
   <updated>2023-07-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/07/24/AdviceToStartAnUltralearningProject</id>
   <content type="html">&lt;p&gt;Some days ago, I got a message from someone starting his journey to become a Software Engineer. He found my post with &lt;a href=&quot;/2020/07/14/UltralearningTakeaways/&quot;&gt;the takeaways from the Ultralearning book&lt;/a&gt; and asked for feedback.&lt;/p&gt;

&lt;p&gt;On the email, my reader explained that he wanted to become a professional Software Engineer with a one-year ultralearning project. Also, he wrote he had a list of resources compiled and already made some progress.&lt;/p&gt;

&lt;p&gt;I want to document my reply to help others and &lt;a href=&quot;/2021/12/06/ItsNotWhatYouRead/&quot;&gt;preserve my keystrokes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is my long reply and how I would start an Ultralearning project:&lt;/p&gt;

&lt;h2 id=&quot;1-set-milestones&quot;&gt;1. Set milestones&lt;/h2&gt;

&lt;p&gt;Keep yourself focused and motivated with milestones.&lt;/p&gt;

&lt;p&gt;For example, after 2 or 3 months of studying, make sure to complete an introductory CS course or have some features of a coding project ready.&lt;/p&gt;

&lt;p&gt;Often we underestimate what we can do in a year or get easily distracted.&lt;/p&gt;

&lt;h2 id=&quot;2-choose-math-subjects-wisely&quot;&gt;2. Choose Math subjects wisely&lt;/h2&gt;

&lt;p&gt;This might be controversial. But don’t get too focused on learning advanced Math.&lt;/p&gt;

&lt;p&gt;Depending on the business domain you’re working on as a Software Engineer, you might not need a lot of Math. Unless you’re working on Computer Graphics, Finance, or Simulations.&lt;/p&gt;

&lt;p&gt;I’d stick to courses on Linear Algebra and Math for Computer Science.&lt;/p&gt;

&lt;h2 id=&quot;3-use-roadmaps-as-inspiration&quot;&gt;3. Use roadmaps as inspiration&lt;/h2&gt;

&lt;p&gt;Find lists of subjects to learn from roadmaps.&lt;/p&gt;

&lt;p&gt;If you search on Google or DuckDuckGo or GitHub “programming roadmap &amp;lt;insert year here&amp;gt;,” you will find good resources. But you don’t need to learn all those subjects at once. Instead, understand how a particular subject or tool fits into the larger picture and when you need it.&lt;/p&gt;

&lt;p&gt;Only zoom in when you need a particular tool.&lt;/p&gt;

&lt;h2 id=&quot;4-write-an-end-to-end-coding-project&quot;&gt;4. Write an end-to-end coding project&lt;/h2&gt;

&lt;p&gt;Write a coding project that reads data from a webpage, calls a backend, persists data into a relational database, and displays it back.&lt;/p&gt;

&lt;p&gt;You will learn a lot from this simple exercise:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;HTML/CSS,&lt;/li&gt;
  &lt;li&gt;a UI library,&lt;/li&gt;
  &lt;li&gt;HTTP/REST,&lt;/li&gt;
  &lt;li&gt;a backend language,&lt;/li&gt;
  &lt;li&gt;SQL, and&lt;/li&gt;
  &lt;li&gt;a database engine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quite a lot!&lt;/p&gt;

&lt;p&gt;ou will be surprised by how many “real” applications boil down to read and write data from and to a database.&lt;/p&gt;

&lt;h2 id=&quot;5-be-consistent&quot;&gt;5. Be consistent&lt;/h2&gt;

&lt;p&gt;I know this is cliche at this point. But…&lt;/p&gt;

&lt;p&gt;Set a regular study time and put it in a calendar. I find the green squares on my GitHub profile inspiring to keep myself in the loop.&lt;/p&gt;

&lt;h2 id=&quot;6-learn-the-tech-and-tools-companies-are-hiring-for&quot;&gt;6. Learn the tech and tools companies are hiring for&lt;/h2&gt;

&lt;p&gt;Probably, you will hear or read people arguing to “learn X instead of Y” or “X pays more than Y.”&lt;/p&gt;

&lt;p&gt;Instead of looking for the best-paying languages, use a more tactical approach, find what companies around you (or on LinkedIn) are looking for, and learn those subjects.&lt;/p&gt;

&lt;p&gt;Or, in any case, it seems there’s always a shortage of COBOL developers. I’ve read they’re well paid.&lt;/p&gt;

&lt;h2 id=&quot;7-keep-a-journal&quot;&gt;7. Keep a journal&lt;/h2&gt;

&lt;p&gt;Keep track of what you learn, the resources you use, and the subjects you find challenging.&lt;/p&gt;

&lt;p&gt;You don’t need anything fancy. A simple .txt file works. Sorry, if you were expecting Notion. I’m a &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;plain-text lover&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I found this advice about the journal on the book “Never Stop Learning” by Bradley R. Staats.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how I would approach a ultralearning project to become a Software Engineer. My last piece of advice is you don’t need to learn everything at once. In the beginning, learn a handful of tools and learn them well. But don’t be afraid of learning something else. Later you could start expanding your toolbox and finding what you like the most.&lt;/p&gt;

&lt;p&gt;I wrote my own &lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;roadmap for intermediate C# developers&lt;/a&gt;. It points to C# resources, but its overall subject structure works for other languages too. This is not for absolute beginners.&lt;/p&gt;

&lt;p&gt;I tried to challenge myself with mini ultralearning projects. I choose to learn enough &lt;a href=&quot;/2020/10/26/ReactIn30Days/&quot;&gt;React&lt;/a&gt; and &lt;a href=&quot;/2020/07/05/LetsGoStudyPlan/&quot;&gt;Go&lt;/a&gt; in 30 days.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy ultralearning!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Five years ago, I wrote my first blog post</title>
   <link href="https://canro91.github.io/2023/07/18/FiveYearsOfBlogging/"/>
   <updated>2023-07-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/07/18/FiveYearsOfBlogging</id>
   <content type="html">&lt;p&gt;Some days ago I found out this &lt;a href=&quot;https://news.ycombinator.com/item?id=35164819&quot;&gt;Hacker News question&lt;/a&gt; about what blogging has done for blog writers. I realized that I published my first blog post five years ago. I’d like to share what blogging has done for me.&lt;/p&gt;

&lt;p&gt;In a past post, I shared &lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;how I started blogging&lt;/a&gt; and the story behind my first post. Long story short: I didn’t want to throw away some hours of Googling.&lt;/p&gt;

&lt;h2 id=&quot;1-what-has-my-blog-done-for-me&quot;&gt;1. What has my blog done for me?&lt;/h2&gt;

&lt;p&gt;I wish I could tell that I could live out of my blog. That’s not the case yet. But it had opened doors here and there.&lt;/p&gt;

&lt;p&gt;After sharing some of my posts on &lt;a href=&quot;https://linkedin.com/in/iamcesaraguirre&quot;&gt;my LinkedIn profile&lt;/a&gt;, I got an invitation to create text-based programming courses on a new teaching platform. I wrote a couple of C# courses there.&lt;/p&gt;

&lt;p&gt;Again from LinkedIn, someone from the Marketing team of a software company reached out to me for a content collaboration. I wrote two sponsor posts here on my blog and others on its company blog.&lt;/p&gt;

&lt;p&gt;On another occasion, an acquaintance set me up for an interview for a full-time opportunity as a software engineer. I declined it, but that interview ended up being another content collaboration. I helped that company to start a Medium publication.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1504691342899-4d92b50853e1?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4MzkzNjA2Nw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;13-inch MacBook Pro&quot; /&gt;

&lt;figcaption&gt;That&apos;s not my laptop. But you get the idea...Photo by &lt;a href=&quot;https://unsplash.com/fr/@supersnapper27?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Super Snapper&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/zIwAchjDirM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-skills-blogging-has-taught-me&quot;&gt;2. Skills blogging has taught me&lt;/h2&gt;

&lt;p&gt;Apart from content collaborations, keeping a blog made learn two skills: online writing and SEO.&lt;/p&gt;

&lt;p&gt;I haven’t updated &lt;a href=&quot;/2018/07/18/AOPWithFody/&quot;&gt;my first post&lt;/a&gt;. It’s right there to remind me how I started. At the time, I had zero experience writing online. I only threw some words into an empty file and put it online.&lt;/p&gt;

&lt;p&gt;I had to learn to use shorter sentences, descriptive subheadings, and clear structure.&lt;/p&gt;

&lt;p&gt;I learned to target my posts to a user search query. Also I learned to distinguish between posts I want to rank and posts where I share some thoughts. This is one of them.&lt;/p&gt;

&lt;p&gt;I stopped writing about whatever came to my mind to follow a topic over a series of posts regularly.&lt;/p&gt;

&lt;h2 id=&quot;3-sources-of-inspiration&quot;&gt;3. Sources of inspiration&lt;/h2&gt;

&lt;p&gt;In all these years, I have received inspiration from others in the process.&lt;/p&gt;

&lt;p&gt;In 2020, I found the &lt;a href=&quot;https://exceptionnotfound.net/guest-writer-program/&quot;&gt;Guest Writer Program from exceptionnotfound.net&lt;/a&gt; and accepted the challenge. I wrote &lt;a href=&quot;https://exceptionnotfound.net/author/cesar-aguirre/&quot;&gt;three guest posts&lt;/a&gt; there. That experience helped me to better structure and format a blog post. Thanks, Matthew, if you ever read this.&lt;/p&gt;

&lt;p&gt;The book &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work by Austin Kleon&lt;/a&gt; inspired me to keep writing. Not only do the end results matter, the process to get there, too. I learned that from the book.&lt;/p&gt;

&lt;p&gt;I follow the mantra: “If something takes you more than 20 minutes to figure out, it should be a post.” I learned that from a YouTube video, I can’t find any more.&lt;/p&gt;

&lt;h2 id=&quot;4-some-of-my-favorite-and-popular-posts&quot;&gt;4. Some of my favorite and popular posts&lt;/h2&gt;

&lt;p&gt;In these five years, I’ve written 152 posts, to be precise. Some blog posts came from my frustrations, curiosity, and learning. Often, I like to think of my blog as &lt;a href=&quot;/2020/07/20/TimeCapsule/&quot;&gt;my own time capsule&lt;/a&gt; and &lt;a href=&quot;/2021/12/06/ItsNotWhatYouRead/&quot;&gt;a tool to preserve my keystrokes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These are some of my favorite posts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;C# Definitive Guide&lt;/a&gt;: This is my roadmap for C# intermediate developers.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2019/03/08/ATaleOfAPdfParser/&quot;&gt;Parsinator: A tale of a PDF parser&lt;/a&gt;: This is about Parsinator, a small project I wrote in record time to keep one of my previous employers onboarding new clients.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;A quick guide to LINQ with examples&lt;/a&gt;: I wrote this one to help a friend. She was preparing for a technical interview. This is an “all you need to know” post. I ended up expanding it into a full series of posts and a text-based course.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;: This is one of the guest posts I originally wrote on exceptionnotfound.net. I expanded it to a whole series of posts about unit testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are some of the most popular ones:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/09/30/FormatSQL/&quot;&gt;Two free tools to format SQL queries&lt;/a&gt;: I got tired of formatting queries manually, so…&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/10/05/CompareDateTimeSQLServer/&quot;&gt;How to compare DateTime without the time part in SQL Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;Four naming conventions for unit tests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/06/29/HowToAddACacheLayer/&quot;&gt;How to add a caching layer with ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voila! That’s my blogging journey over these five years. I hope you stick around for the “Ten years ago, I published my first post” reflection.&lt;/p&gt;

&lt;p&gt;If you ask me where my blog will take me, I’d say: “dunno, let’s find out.”&lt;/p&gt;

&lt;p&gt;Thanks to all the heroes who contacted me to point out typos or a wrong variable name in my posts.&lt;/p&gt;

&lt;p&gt;If I have helped you with my writing, feel free to &lt;a href=&quot;/contact&quot;&gt;contact me&lt;/a&gt; to say Hi. And, if you want to support my work, &lt;a href=&quot;https://www.educative.io/profile/view/5684280228839424&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;check my courses on Educative&lt;/a&gt; or leave a tip on one of my ebooks on &lt;a href=&quot;https://imcsarag.gumroad.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my Gumroad page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy reading!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: NDC Conference</title>
   <link href="https://canro91.github.io/2023/07/10/MondayLinks/"/>
   <updated>2023-07-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/07/10/MondayLinks</id>
   <content type="html">&lt;p&gt;This is another episode where I share the talks from NDC Conference I watched and liked. This time is about JavaScript, History, and Design.&lt;/p&gt;

&lt;h2 id=&quot;how-javascript-happened-a-short-history-of-programming-languages---mark-rendle&quot;&gt;How JavaScript Happened: A Short History of Programming Languages - Mark Rendle&lt;/h2&gt;

&lt;p&gt;This is a journey from FORTRAN to ALGOL to LISP to JavaScript. It explains why we still use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; for conditional, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; for loops, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; for multiplication. Spoiler alert: It’s because of FORTRAN.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/hEdzaIa4Heg?start=1471&amp;amp;rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;apache-kafka-in-1-hour-for-c-developers---guilherme-ferreira&quot;&gt;Apache Kafka in 1 hour for C# Developers - Guilherme Ferreira&lt;/h2&gt;

&lt;p&gt;Clusters, Topics, Partitions, producers/consumers? This is a good first-time introduction to Kafka. The presenter uses &lt;a href=&quot;https://github.com/Farfetch/kafkaflow&quot;&gt;kafkaflow&lt;/a&gt; and &lt;a href=&quot;https://github.com/confluentinc/confluent-kafka-dotnet&quot;&gt;confluent-kafka-dotnet&lt;/a&gt; for the demo application.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/4xpjlqIlfY8?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;keynote-why-web-tech-is-like-this---steve-sanderson&quot;&gt;Keynote: Why web tech is like this - Steve Sanderson&lt;/h2&gt;

&lt;p&gt;I found this one on r/programming (before the Reddit blackout) Informative! It feels like time traveling through operating systems and tools to create a Web page.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/2buWVsQ7stk?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;pilot-critical-decision-making-skills---clifford-agius&quot;&gt;Pilot Critical Decision Making skills - Clifford Agius&lt;/h2&gt;

&lt;p&gt;The lesson from this one is to come up with a list of things that could go wrong and prepare and train for that. Follow TDODAR approach: Time, Diagnosis, Options, Decision, Assign, and Review.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/ahl6KeqQDlE?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;intentional-code---minimalism-in-a-world-of-dogmatic-design&quot;&gt;Intentional Code - Minimalism in a World of Dogmatic Design&lt;/h2&gt;

&lt;p&gt;I like the idea that “software really is literature.” Not in the sense of literate programming but in the sense of a narrative to express idea where every line of code matters. I like the example of how a piece of code improves by only removing a few blank lines.&lt;/p&gt;

&lt;p&gt;Another idea I liked is: “You don’t want everything to look the same.” We don’t want all applications to use Domain-Driven Design with Event Sourcing and microservices. Often architectural patterns only add to cognitive load and extra complexity.&lt;/p&gt;

&lt;p&gt;The presenter suggests: “sitting and looking at it (at a piece of code) and working out how it makes you feel. And then when you feel something, try to understand why it feels that way.”&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/vw2XffPmlYo?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Voilà! Another Monday Links. What tech conferences do you follow? Do you also follow NDC Conference? What are your favorite presentations? Until next Monday Links.&lt;/p&gt;

&lt;p&gt;In the meantime, don’t miss the previous &lt;a href=&quot;/2023/06/12/MondayLinks/&quot;&gt;Monday Links on Personal Moats, Unfair Advantage, and Quitting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to pass a DataTable as a parameter with OrmLite</title>
   <link href="https://canro91.github.io/2023/06/26/PassDataTableOrmLite/"/>
   <updated>2023-06-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/06/26/PassDataTableOrmLite</id>
   <content type="html">&lt;p&gt;These days I use OrmLite a lot. Almost every single day. In one of my client’s projects, OrmLite is the defacto ORM. Today I needed to pass a list of identifiers as a DataTable to an OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlExpression&lt;/code&gt;. I didn’t want to write plain old SQL queries and use the embedded Dapper methods inside OrmLite. This is what I found out after a long debugging session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To pass a DataTable with a list of identifiers as a parameter to OrmLite methods, create a custom converter for the DataTable type. Then use ConvertToParam() to pass it as a parameter to methods that use raw SQL strings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As an example, let’s find all movies from a list of director Ids. I know a simple JOIN will get our backs covered here. But bear with me. Let’s imagine this is a more involved query.&lt;/p&gt;

&lt;h2 id=&quot;1-create-two-entities-and-a-table-type&quot;&gt;1. Create two entities and a table type&lt;/h2&gt;

&lt;p&gt;These are the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt; classes,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Director&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;References&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// OrmLite expects a foreign key back to the Movie table&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In our database, let’s define the table type for our list of identifiers. Like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TYPE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntList&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1519389950473-47ba0277781c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4MTUxMzI5OA&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;bunch of laptops on a table&quot; /&gt;

&lt;figcaption&gt;A data table...Photo by &lt;a href=&quot;https://unsplash.com/@marvelous?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Marvin Meyer&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/SYTO3xs06fU?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-pass-a-datatable-to-a-sqlexpression&quot;&gt;2. Pass a DataTable to a SqlExpression&lt;/h2&gt;

&lt;p&gt;Now, to the actual OrmLite part,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NUnit.Framework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceStack.DataAnnotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Data.SqlClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PlayingWithOrmLiteAndDataTables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DataTableAsParameterTest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LookMaItWorks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 1. Register our custom converter&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OrmLiteConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DialectProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlServerDialect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OrmLiteConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DialectProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SqlServerDataTableParameterConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                                          ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;...Any SQL Server connection string here...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrmLiteConnectionFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 2. Populate some movies&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;titanic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;James Cameron&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;titanic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;privateRyan&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Saving Private Ryan&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Steven Spielberg&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;privateRyan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pulpFiction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Pulp Fiction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Quentin Tarantino&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pulpFiction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 3. Populate datable with some Ids&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieIds&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DataTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;movieIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;movieIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This should be Saving Private Ryan&apos;s Id&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 4. Write the SqlExpression&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Imagine this is a more complex query. I know!&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableParam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConvertToParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movieIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CustomJoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;INNER JOIN &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ids ON Director.MovieId = ids.Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We&apos;re cheating here. We know the table name! I know.&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 5. Enjoy!&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spielberg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spielberg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spielberg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we first registered our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlServerDataTableParameterConverter&lt;/code&gt;. More on that later!&lt;/p&gt;

&lt;p&gt;After populating some records, we wrote a query using OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlExpression&lt;/code&gt; syntax and a JOIN to our table parameter using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomJoin()&lt;/code&gt;. Also, we needed to convert our DataTable into a parameter with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConvertToParam()&lt;/code&gt; method before referencing it.&lt;/p&gt;

&lt;p&gt;We cheated a bit. Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt; class has the same name as our table. If that’s not the case, we could use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetQuotedTableName()&lt;/code&gt; method, for example.&lt;/p&gt;

&lt;h2 id=&quot;3-write-an-ormlite-custom-converter-for-datatable&quot;&gt;3. Write an OrmLite custom converter for DataTable&lt;/h2&gt;

&lt;p&gt;And this is our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlServerDataTableParameterConverter&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This converter only works when passing DataTable&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// as a parameter to OrmLite methods. It doesn&apos;t work&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// with OrmLite LoadSelectAsync method.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlServerDataTableParameterConverter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrmLiteConverter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ColumnDefinition&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NotImplementedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Only use to pass DataTable as parameter.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitDbParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDbDataParameter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlParameter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sqlParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SqlDbType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlDbType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Structured&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sqlParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;dbo.IntList&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//                       ^^^^^ &lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// This should be our table type name&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// The same name as in the database&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This converter only works when passing DataTable as a parameter. That’s why it has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NotImplementedException&lt;/code&gt;. I tested it with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SelectAsync()&lt;/code&gt; method. It doesn’t work with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelectAsync()&lt;/code&gt; method. This last method doesn’t parameterize internal queries. It will bloat our database’s plan cache. Take a look at OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelectAsync()&lt;/code&gt; source code on GitHub &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/e8e7b1e1f450506c4b2ee052fcc1904966f161d7/ServiceStack.OrmLite/src/ServiceStack.OrmLite/Async/OrmLiteReadCommandExtensionsAsync.cs#L409&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/blob/6298a3b84c41e9a5fe4dcba1ed7ad48cc79b69e2/ServiceStack.OrmLite/src/ServiceStack.OrmLite/Support/LoadList.cs#L48&quot;&gt;here&lt;/a&gt; to see what I mean.&lt;/p&gt;

&lt;p&gt;To make this converter work with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadSelectAsync()&lt;/code&gt;, we would need to implement the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToQuotedString()&lt;/code&gt; and return the DataTable content as a comma-separated list of identifiers. Exercise left to the reader!&lt;/p&gt;

&lt;h2 id=&quot;4-write-a-convenient-extension-method&quot;&gt;4. Write a convenient extension method&lt;/h2&gt;

&lt;p&gt;And, for compactness, let’s put that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomJoin()&lt;/code&gt; into a beautiful extension method that infers the table and column name to join to,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlExpressionExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JoinToDataTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataTable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sourceDefinition&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ModelDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Definition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConvertToParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Expected SQL: INNER JOIN @0 ON &quot;Parent&quot;.&quot;EvaluatedExpression&quot;= &quot;@0&quot;.Id&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onExpression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;ON (&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SqlTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SqlColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &quot;&quot;{parameter}&quot;&quot;.&quot;&quot;Id&quot;&quot;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customSql&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;INNER JOIN &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CustomJoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customSql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can use it like,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// var query = db.From&amp;lt;Director&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// var tableParam = query.ConvertToParam(movieIds);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// query = query.CustomJoin(@$&quot;INNER JOIN {tableParam} ids ON Director.MovieId = ids.Id&quot;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// After: &lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JoinToDataTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That is what I learned (or hacked) today. Things we only find out when reading the source code of our libraries. Another thought: the thing with ORMs is the moment we need to write complex queries, we stretch out ORM features until they break. Often, we’re better off &lt;a href=&quot;/2021/03/08/HowNotToWriteDynamicSQL/&quot;&gt;writing dynamic SQL queries&lt;/a&gt;. I know, I know! Nobody wants to write dynamic SQL queries by hand. Maybe ask ChatGPT?&lt;/p&gt;

&lt;p&gt;If you want to read more about OrmLite and its features, check &lt;a href=&quot;/2022/12/11/AuditFieldsWithOrmLite/&quot;&gt;how to automatically insert and update audit fields with OrmLite&lt;/a&gt; and &lt;a href=&quot;/2022/12/13/LessonsOnHangfireAndOrmLite/&quot;&gt;some lessons I learned after working with OrmLite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Personal Moats, Unfair Advantage, and Quitting</title>
   <link href="https://canro91.github.io/2023/06/12/MondayLinks/"/>
   <updated>2023-06-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/06/12/MondayLinks</id>
   <content type="html">&lt;p&gt;This is a career-only episode. These are five links I found interesting in the last month.&lt;/p&gt;

&lt;h2 id=&quot;build-personal-moats&quot;&gt;Build Personal Moats&lt;/h2&gt;

&lt;p&gt;From this post, the best career advice is to build a personal moat: &lt;em&gt;“a set of unique and accumulating competitive advantages in the context of your career.”&lt;/em&gt; It continues describing good moats and how to find yours.&lt;/p&gt;

&lt;p&gt;About personal moats:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;“Ask others: What’s something that’s easy for me to do but hard for others?”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“Ideally you want this personal moat to help you build career capital in your sleep.”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“If you were magically given 10,000 hours to be amazing at something, what would it be? The more clarity you have on this response, the better off you’ll be.”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://eriktorenberg.substack.com/p/build-personal-moats&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;want-an-unfair-advantage-in-your-tech-career-consume-content-meant-for-other-roles&quot;&gt;Want an unfair advantage in your tech career? Consume content meant for other roles&lt;/h2&gt;

&lt;p&gt;This post is to build a competitive advantage by consuming content targeted to other roles. This is a mechanism to create more empathy, gain understanding, and better work in cross-functional teams, among other reasons. It also suggests a list of roles we can start learning about.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://matthewgrohman.substack.com/p/want-an-unfair-advantage-in-your&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1582140110470-606646cd836c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4NTIwNTUzMQ&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;man in white button up shirt sitting at the table&quot; /&gt;

&lt;figcaption&gt;&quot;Hey boss. I quit. Good luck&quot; Photo by &lt;a href=&quot;https://unsplash.com/@bostonpubliclibrary?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Boston Public Library&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/3P1BYWVkcJo?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;career-advice-no-one-gave-me-give-a-lot-of-notice-when-you-quit&quot;&gt;Career Advice No One Gave Me: Give a Lot of Notice When You Quit&lt;/h2&gt;

&lt;p&gt;This is gold! There’re lots of posts on the Internet about interviewing, but few about quitting. This one is about how to quit leaving doors open. It has concrete examples to “drop the bomb.”&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://davidlaprade.github.io/give-a-lot-of-notice&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;my-20-year-career-is-technical-debt-or-deprecated&quot;&gt;My 20-Year Career is Technical Debt or Deprecated&lt;/h2&gt;

&lt;p&gt;Reading this post, I realized I jumped to companies to always rewrite old applications. An old ASP.NET WebForms to a Console App. (Don’t ask me why!) An old ASP.NET WebForms again to an ASP.NET Web API project. An old Python scheduler to an ASP.NET Core project with HostedServices. History repeats itself, I guess. We’re writing legacy applications of tomorrow.&lt;/p&gt;

&lt;p&gt;Let’s embrace that, quoting the post, &lt;em&gt;“Given enough time, all your code will get deleted.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.visionarycto.com/p/my-20-year-career-is-technical-debt&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-you-give-up-when-moving-into-engineering-management&quot;&gt;What you give up when moving into engineering management&lt;/h2&gt;

&lt;p&gt;Being a Manager requires different skills than being an Individual Contributor. Often people get promoted to the Management track (without any training) only because they’re good developers. Arrrgggg! I’ve seen managers that are only good developers…and projects at risk because of that. This post shares why it’s hard to make the change and what we lost by moving to the Management track, focus time, for example.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://stackoverflow.blog/2022/02/23/what-you-give-up-when-moving-into-engineering-management/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links. Do you think you have a personal moat or an unfair advantage? What is it? What are your quitting experiences? Until next Monday Links.&lt;/p&gt;

&lt;p&gt;In the meantime, don’t miss the previous &lt;a href=&quot;/2023/05/01/MondayLinks/&quot;&gt;Monday Links on Interviewing, Zombies, and Burnout&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Let&apos;s refactor a test: Speed up a slow test suite</title>
   <link href="https://canro91.github.io/2023/05/29/SpeedingUpSomeTests/"/>
   <updated>2023-05-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/05/29/SpeedingUpSomeTests</id>
   <content type="html">&lt;p&gt;Do you have fast unit tests? This is how I speeded up a slow test suite from one of my client’s projects by reducing the delay between retry attempts and initializing slow-to-build dependencies only once. There’s a lesson behind this refactoring session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make sure to have a fast test suite that every developer could run after every code change. The slower the tests, the less frequently they’re run.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I learned to have some metrics before rushing to optimize anything. I learned it while trying to &lt;a href=&quot;/2020/09/23/TheSlowRoomSearch/&quot;&gt;optimize a slow room searching feature&lt;/a&gt;. These are the tests and their execution time before any changes:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-05-29-SpeedingUpSomeTests/Before.png&quot; alt=&quot;Slow tests&quot; /&gt;
    &lt;figcaption&gt;Slow tests&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Of course, I blurred some names for obvious reasons. I focused on two projects: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Api.Tests&lt;/code&gt; (3.3 min) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReservationQueue.Tests&lt;/code&gt; (18.9 sec).&lt;/p&gt;

&lt;p&gt;I had a slower test project, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data.Tests&lt;/code&gt;. It contained integration tests using a real database. Probably those tests could benefit from &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;simple test values&lt;/a&gt;. But I didn’t want to &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;tune stored procedures or queries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is what I found and did to speed up this test suite.&lt;/p&gt;

&lt;h2 id=&quot;step-1-reduce-delays-between-retries&quot;&gt;Step 1: Reduce delays between retries&lt;/h2&gt;

&lt;p&gt;Inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Api.Tests&lt;/code&gt;, I found tests for services with a retry mechanism. And, inside the unit tests, I had to wait more than three seconds between every retry attempt. C’mon, these are unit tests! Nobody needs or wants to wait between retries here.&lt;/p&gt;

&lt;p&gt;My first solution was to reduce the delay between retry attempts to zero.&lt;/p&gt;

&lt;h3 id=&quot;set-retrywaitseconds--0&quot;&gt;Set retryWaitSeconds = 0&lt;/h3&gt;

&lt;p&gt;Some tests built retry policies manually and passed them to services. I only needed to pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; as a delay. Like this,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-05-29-SpeedingUpSomeTests/ZeroDelay.png&quot; alt=&quot;Diff of setting retryWaitSecond variable to zero&quot; /&gt;
    &lt;figcaption&gt;Making retryWaitSeconds = 0&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;A simple &lt;a href=&quot;/2022/12/10/ReplaceKeywordInFile/&quot;&gt;Bash one-liner to find and replace a pattern&lt;/a&gt; got my back covered here.&lt;/p&gt;

&lt;h3 id=&quot;pass-retryoptions-without-delay&quot;&gt;Pass RetryOptions without delay&lt;/h3&gt;

&lt;p&gt;Some other tests used an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EventHandler&lt;/code&gt; base class. After running a command handler wrapped in a database transaction, we needed to call other internal microservices. We used event handlers for that. This is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EventHandlerBase&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EventHandlerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEventHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RetryOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_retryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EventHandlerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_retryOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RetryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// By default, it has:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// MaxRetries = 2&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// RetryDelayInSeconds = 3&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExecuteAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildRetryPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ExecuteAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Sorry, something wrong happened...&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Log things here like good citizens of the world...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AsyncPolicy&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildRetryPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpRequestException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WaitAndRetryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_retryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxRetries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;retryAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_retryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RetryDelayInSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retryAttempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//                ^^^^^&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retryCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
                    &lt;span class=&quot;c1&quot;&gt;// Log things here like good citizens of the world...&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetRetryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RetryOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m_retryOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice one thing: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EventHandlerBase&lt;/code&gt; didn’t receive a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RetryOptions&lt;/code&gt; in its constructor. All event handlers had, by default, a 3-second delay. Even the ones inside unit tests. Arrrgggg! And the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EventHandlerBase&lt;/code&gt; used an exponential backoff. Arrrgggg! That explained why I had those slow tests.&lt;/p&gt;

&lt;p&gt;The perfect solution would have been to make all child event handlers receive the right &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RetryOptions&lt;/code&gt;. But it would have required changing the Production code and probably retesting some parts of the app.&lt;/p&gt;

&lt;p&gt;Instead, I went through all the &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;builder methods inside tests&lt;/a&gt; and passed a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RetryOptions&lt;/code&gt; without delay. Like this,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-05-29-SpeedingUpSomeTests/WithoutDelay.png&quot; alt=&quot;Adding a RetryOptions&quot; /&gt;
    &lt;figcaption&gt;Adding a RetryOptions&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After removing that delay between retries, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Api.Tests&lt;/code&gt; ran faster.&lt;/p&gt;

&lt;h2 id=&quot;step-2-initialize-automapper-only-once&quot;&gt;Step 2: Initialize AutoMapper only once&lt;/h2&gt;

&lt;p&gt;Inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReservationQueue.Tests&lt;/code&gt;, the other slow test project, I found some tests using AutoMapper. Oh, boy! AutoMapper! I have a love-and-hate relationship with AutoMapper. I shared about AutoMapper in &lt;a href=&quot;/2022/03/14/MondayLinks/&quot;&gt;a past Monday Links episode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some of the tests inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReservationQueue.Tests&lt;/code&gt; looked like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ACoolTestClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestBuilder&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapper&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ServiceCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;

                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BuildServiceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetRequiredService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceToTest&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ServiceToTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//                       ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetSomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Make the fake SomeService instance return some hard-coded values...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ATest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetSomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Assert something here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Imagine more tests that follow the same pattern...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These tests used a private &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestBuilder&lt;/code&gt; class to create a service with all its dependencies replaced by fakes. Except for AutoMapper’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMapper&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMapper&lt;/code&gt;, these tests had a property that used the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddMapping()&lt;/code&gt; method used in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; file. It was an extension method with hundreds and hundreds of type mappings. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapperConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Literally hundreds of single-type mappings here...&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Hundreds and hundreds...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AssertConfigurationIsValid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-05-29-SpeedingUpSomeTests/AddMapping.png&quot; alt=&quot;A collapsed hundred-line AddMapping method&quot; /&gt;
    &lt;figcaption&gt;Look at the line numbers on the left!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The thing is that every single test created a new instance of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestBuilder&lt;/code&gt; class. And, by extension, an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMapper&lt;/code&gt; for every test. And creating an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMapper&lt;/code&gt; is expensive. Arrrgggg!&lt;/p&gt;

&lt;p&gt;A better solution would have been to use AutoMapper Profiles and only load the profiles needed in each test class. That would have been a long and painful refactoring session.&lt;/p&gt;

&lt;h3 id=&quot;use-mstest-classinitialize-attribute&quot;&gt;Use MSTest ClassInitialize attribute&lt;/h3&gt;

&lt;p&gt;Instead of creating an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMapper&lt;/code&gt; when running every test, I did it only once per test class. I used MSTest &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ClassInitialize]&lt;/code&gt; attribute. It decorates a static method that runs before all the test methods of a class. That was exactly what I needed.&lt;/p&gt;

&lt;p&gt;To learn about all MSTest attributes, check &lt;a href=&quot;https://www.meziantou.net/mstest-v2-test-lifecycle-attributes.htm&quot;&gt;Meziantou’s MSTest v2: Test lifecycle attributes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My sample test class using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ClassInitialize]&lt;/code&gt; looked like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ACoolTestClass&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClassInitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestClassSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                 ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ServiceCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BuildServiceProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetRequiredService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestBuilder&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// No more IMapper initializations here&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServiceToTest&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ServiceToTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//                       ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetSomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Return some hardcoded values from ISomeService methods...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Same tests as before...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I needed to replicate this change in other test classes that used AutoMapper.&lt;/p&gt;

&lt;p&gt;After reducing the delay between retry attempts and creating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMapper&lt;/code&gt; once per test class, these were the final execution times,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-05-29-SpeedingUpSomeTests/After.png&quot; alt=&quot;List of tests inside Visual Studio&quot; /&gt;
    &lt;figcaption&gt;Faster tests&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;That’s under a minute! They used to run in ~3.5 minutes.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how I speeded up this test suite. Apart from reducing delays between retry attempts in our tests and initializing AutoMapper once per test class, the lesson to take home is to have a fast test suite. A test suite we can run after every code change. Because the slower the tests, the less frequently we run them. And we want our backs covered by tests all the time.&lt;/p&gt;

&lt;p&gt;To read more about unit testing, check refactoring sessions to &lt;a href=&quot;/2022/12/22/TestingDuplicatedEmails/&quot;&gt;remove duplicated emails&lt;/a&gt; and &lt;a href=&quot;/2023/05/15/TestingEmailStatusUpdates/&quot;&gt;update email statuses&lt;/a&gt;. And don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover from naming conventions to best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Let&apos;s refactor a test: Update email statuses</title>
   <link href="https://canro91.github.io/2023/05/15/TestingEmailStatusUpdates/"/>
   <updated>2023-05-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/05/15/TestingEmailStatusUpdates</id>
   <content type="html">&lt;p&gt;Let’s continue refactoring some tests for an email component. Last time, we refactored two tests that &lt;a href=&quot;/2022/12/22/TestingDuplicatedEmails/&quot;&gt;remove duplicated email addresses&lt;/a&gt; before sending an email. This time, let’s refactor two more tests. But these ones check that we change an email status once we receive a “webhook” from a third-party email service. Let’s refactor them.&lt;/p&gt;

&lt;h2 id=&quot;here-are-the-tests-to-refactor&quot;&gt;Here are the tests to refactor&lt;/h2&gt;

&lt;p&gt;If you missed the &lt;a href=&quot;/2022/12/22/TestingDuplicatedEmails/&quot;&gt;last refactoring session&lt;/a&gt;, these tests belong to an email component in a Property Management Solution. This component stores all emails before sending them and keeps track of their status changes.&lt;/p&gt;

&lt;p&gt;These two tests check we change the recipient status to either “delivered” or “complained.” Of course, the original test suite had more tests. We only need one or two tests to prove a point.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AcmeCorp.Email.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UpdateStatusCommandHandlerTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle_ComplainedStatusOnlyOnOneRecipient_UpdatesStatuses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withComplainedStatusOnlyOnCc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                         ^^^^^&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastDeliveryStatus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadyToBeSent&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastDeliveryStatus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Complained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle_DeliveredStatusToBothRecipients_UpdatesStatuses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withDeliveredStatusOnBoth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                         ^^^^^&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastDeliveryStatus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delivered&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastDeliveryStatus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delivered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateStatusCommandHandler&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BuildEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateStatusCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateStatusCommand&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;withComplainedStatusOnlyOnCc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;withDeliveredStatusOnBoth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Imagine more flags for other combination&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// of statuses. Like opened, bounced, and clicked&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Imagine building a large object graph here&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// based on the parameter flags&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateStatusCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;A Subject&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;A Body&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;to@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cc@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I slightly changed some test and method names. But those are some of the real tests I had to refactor.&lt;/p&gt;

&lt;p&gt;What’s wrong with those tests? Did you notice it?&lt;/p&gt;

&lt;p&gt;These tests use &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;Moq to create a fake&lt;/a&gt; for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEmailRepository&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildHandler()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildCommand()&lt;/code&gt; factory methods to &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;reduce the noise&lt;/a&gt; and keep our test simple.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1634562876572-5abe57afcceb?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4MDY1MjQxMQ&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;A pen sitting in top of a piece of paper&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@towfiqu999999?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Towfiqu barbhuiya&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/6FpGIdn45_A?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;whats-wrong&quot;&gt;What’s wrong?&lt;/h2&gt;

&lt;p&gt;Let’s take a look at the first test. Inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; method, why is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Recipient[1]&lt;/code&gt; the one expected to have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Complained&lt;/code&gt; status? what if we change the order of recipients?&lt;/p&gt;

&lt;p&gt;Based on the &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;scenario in the test name&lt;/a&gt;, &lt;em&gt;“complained status only on one recipient”&lt;/em&gt;, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;withComplainedStatusOnlyOnCc&lt;/code&gt; parameter passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildCommand()&lt;/code&gt;, we might think &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Recipient[1]&lt;/code&gt; is the email’s cc address. But, the test hides the order of recipients. We would have to inspect the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildHandler()&lt;/code&gt; method to see the email injected into the handler and check the order of recipients.&lt;/p&gt;

&lt;p&gt;In the second test, since we expect all recipients to have the same status, we don’t care much about the order of recipients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We shouldn’t hide anything in builders or helpers and later use those hidden assumptions in other parts of our tests. That makes our tests difficult to follow. And we shouldn’t make our readers decode our tests.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;explicit-is-better-than-implicit&quot;&gt;Explicit is better than implicit&lt;/h2&gt;

&lt;p&gt;Let’s rewrite our tests to avoid passing flags like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;withComplainedStatusOnlyOnCc&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;withDeliveredStatusOnBoth&lt;/code&gt;, and verifying on a hidden recipient order. Instead of passing flags for every possible combination of status to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildCommand()&lt;/code&gt;, let’s create one &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;object mother&lt;/a&gt; per status explicitly passing the email addresses we want.&lt;/p&gt;

&lt;p&gt;Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UpdateStatusCommandHandlerTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle_ComplainedStatusOnlyOnOneRecipient_UpdatesStatuses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;addresses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;to@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cc@email.com&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;With&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EmailFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;//    ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateStatusCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ComplaintFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;to@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                ^^^^^&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyUpdatedStatusFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;to@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Complained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cc@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadyToBeSent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle_DeliveredStatusToBothRecipients_UpdatesStatuses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;addresses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;to@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cc@email.com&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;With&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EmailFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
                            &lt;span class=&quot;c1&quot;&gt;//    ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdateStatusCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DeliveredTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                ^^^^^&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                
        &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyUpdatedStatusForAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delivered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First, instead of creating a fake &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmailRepository&lt;/code&gt; with a hidden email object, we wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;With()&lt;/code&gt; method. And to make things more readable, we renamed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuilEmail()&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmailFor()&lt;/code&gt; and passed the destinations explicitly to it. We can read it like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mock.With(EmailFor(anAddress))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, instead of using a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildCommand()&lt;/code&gt; with a flag for every combination of statuses, we created one object mother per status: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ComplaintFrom()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeliveredTo()&lt;/code&gt;. Again, we passed the email addresses we expected to have either complained or delivered statuses.&lt;/p&gt;

&lt;p&gt;Lastly, for our Assert part, we created two &lt;a href=&quot;/2021/08/16/WriteCustomAssertions/&quot;&gt;custom Verify methods&lt;/a&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyUpdatedStatusFor()&lt;/code&gt; and  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyUpdatedStatusForAll()&lt;/code&gt;. In the first test, we passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyUpdatedStatusFor()&lt;/code&gt; an array of tuples with the email address and its expected status.&lt;/p&gt;

&lt;p&gt;Voilà! That was another refactoring session. When we write unit tests, we should strive for a balance between implicit code to reduce the noise in our tests and explicit code to make things easier to follow.&lt;/p&gt;

&lt;p&gt;In the original version of these tests, we hid the order of recipients when building emails. But then we relied on that order when writing assertions. Let’s not be like magicians pulling code we had hidden somewhere else.&lt;/p&gt;

&lt;p&gt;Also, let’s use extension methods and object mothers like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;With()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmailFor()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeliveredTo()&lt;/code&gt; to create a small “language” in our tests, striving for readability. The next person writing tests will copy the existing ones. That will make his life easier.&lt;/p&gt;

&lt;p&gt;For more refactoring sessions, check these two: &lt;a href=&quot;/2022/12/08/TestingOAuthConnections/&quot;&gt;store and update OAuth connections&lt;/a&gt; and &lt;a href=&quot;/2021/08/02/LetsRefactorATest/&quot;&gt;generate payment reports&lt;/a&gt;. And don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover from naming conventions to best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Interviewing, Zombies, and Burnout</title>
   <link href="https://canro91.github.io/2023/05/01/MondayLinks/"/>
   <updated>2023-05-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/05/01/MondayLinks</id>
   <content type="html">&lt;p&gt;For this Monday Links, I’d like to share five reads about interviewing, motivation, and career. These are five articles I found interesting in the past month or two.&lt;/p&gt;

&lt;h2 id=&quot;programming-interviews-turn-normal-people-into-a-holes&quot;&gt;Programming Interviews Turn Normal People into A-Holes&lt;/h2&gt;

&lt;p&gt;This is a good perspective on the hiring process. But from the perspective of someone who was the hiring manager. Two things I like about this one: &lt;em&gt;“Never ask for anything that can be googled,”&lt;/em&gt; and &lt;em&gt;“Decide beforehand what questions you will ask because I find that not given any instructions, people will resort to asking trivia or whatever sh*t library they are working on.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve been in those interviews that feel like an interrogatory. The only thing missing was a table in the middle of a dark room with a two-way mirror. Like in spy movies. Arrrggg!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://new.pythonforengineers.com/blog/programming-interviews-turn-normal-people-into-a-holes/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;demotivating-a-skilled-programmer&quot;&gt;Demotivating a (Skilled) Programmer&lt;/h2&gt;

&lt;p&gt;From this article, a message for bosses: &lt;em&gt;“…(speaking about salaries, dual monitors, ping pong tables) These things are ephemeral, though. If you don’t have him working on the core functionality of your product, with tons of users, and an endless supply of difficult problems, all of the games of ping pong in the world won’t help.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://levelup.gitconnected.com/demotivating-a-skilled-programmer-6465cae26d2a&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1577894947058-cfdae4276bef?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4Mjc4ODAwMw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&quot; alt=&quot;Workers in a textile factory in the 30s&quot; /&gt;

&lt;figcaption&gt;Simpson&apos;s Gloves Pty Ltd, Richmond, circa 1932. Photo by &lt;a href=&quot;https://unsplash.com/@museumsvictoria?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Museums Victoria&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/7YUvAUbfSV0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;how-to-turn-software-engineers-into-zombies&quot;&gt;How to Turn Software Engineers Into Zombies&lt;/h2&gt;

&lt;p&gt;This is a good post with a sarcastic tone and a good lesson. These are some ideas to turn software engineers into walking dead bodies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;“Always give them something to implement, not to solve”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“When it comes time for promotion come up with some credible silly process they have to go through in order to get something”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“If you keep hiring, you will always pay less per employee.”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had to confess that I never saw the previous third point coming. Those are only three. The post has even more.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://elsoncorreia.medium.com/how-to-turn-software-engineers-into-zombies-bcd7d3f90bd8&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-spot-signs-of-burnout-culture-before-you-accept-a-job&quot;&gt;How to Spot Signs of Burnout Culture Before You Accept a Job&lt;/h2&gt;

&lt;p&gt;We only have one chance of giving a first impression. Often the first impression we have about one company is the job listing itself. This post shows some clues to read between the lines to detect toxic culture from companies.&lt;/p&gt;

&lt;p&gt;I ran from companies with “work under pressure” or “fast pace changing environment” anywhere in the job description. Often that screams: “We don’t know what we’re doing, but we’re already late.” Arrgggg!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lifehacker.com/how-to-spot-signs-of-burnout-culture-before-you-accept-1850286182&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;numbers-to-know-for-managing-software-teams&quot;&gt;Numbers To Know For Managing (Software Teams)&lt;/h2&gt;

&lt;p&gt;I read this one with a bit of skepticism. I was expecting: sprint velocity, planned story points, etc. But I found some interesting metrics, like _five is the number of comments on a document before turning it into a meeting” and “one is the number of times to reverse a resignation.”&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://staysaasy.com/management/2023/03/20/numbers-to-manage-by.html&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links. Have you ever found those interrogatories? Sorry, I meant interviews. Do your company track sprint velocity and story points? What metrics do they track instead? Until next Monday Links.&lt;/p&gt;

&lt;p&gt;In the meantime, don’t miss the previous &lt;a href=&quot;/2023/01/23/MondayLinks/&quot;&gt;Monday Links on Passions, Estimates, and Methodologies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Goodbye, NullReferenceException: Separate State in Separate Objects</title>
   <link href="https://canro91.github.io/2023/04/03/SeparateStateIntoSeparateObjects/"/>
   <updated>2023-04-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/04/03/SeparateStateIntoSeparateObjects</id>
   <content type="html">&lt;p&gt;So far in this series about NullReferenceException, we have used &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;nullable operators and C# 8.0 Nullable References&lt;/a&gt; to avoid &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and learned about &lt;a href=&quot;/2023/03/20/UseOptionInsteadOfNull/&quot;&gt;the Option type&lt;/a&gt; as an alternative to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s see how to design our classes to avoid &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; when representing optional values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instead of writing a large class with methods that expect some nullable properties to be not null at some point, we’re better off using separate classes to avoid dealing with null and getting NullReferenceException.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;multiple-state-in-the-same-object&quot;&gt;Multiple state in the same object&lt;/h2&gt;

&lt;p&gt;Often we keep all possible combinations of properties of an object in a single class.&lt;/p&gt;

&lt;p&gt;For example, on an e-commerce site, we create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; class with a name, password, and credit card. But since we don’t need the credit card details to create new users, we declare the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; property as nullable.&lt;/p&gt;

&lt;p&gt;Let’s write a class to represent either regular or premium users. We should only store credit card details for premium users to charge a monthly subscription.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SaltedPassword&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//              ^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Only for Premium users. We declare it nullable&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BecomePremium&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Imagine we sent an email and validate credit card&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// details, etc&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChargeMonthlySubscription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// CreditCard might be null here.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Nothing is preventing us from calling it&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// for regular users&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Boooom, NullReferenceException&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; property only has value for premium users. We expect it to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; for regular users. And nothing is preventing us from calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChargeMonthlySubscription()&lt;/code&gt; with regular users (when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;). We have a potential source of NullReferenceException.&lt;/p&gt;

&lt;p&gt;We ended up with a class with nullable properties and methods that only should be called when some of those properties aren’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChargeMonthlySubscription()&lt;/code&gt;, we could add some null checks before using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; property. But, if we have other methods that need other properties not to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, our code will get bloated with null checks all over the place.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1537806078416-64d8c0147e1e?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4MDY1MDk5Ng&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Green grass near the gray road&quot; /&gt;

&lt;figcaption&gt;Let&apos;s separate our state...Photo by &lt;a href=&quot;https://unsplash.com/@willfrancis?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Will Francis&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/Rm3nWQiDTzg?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;separate-state-in-separate-objects&quot;&gt;Separate State in Separate Objects&lt;/h2&gt;

&lt;p&gt;Instead of checking for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChargeMonthlySubscription()&lt;/code&gt;, let’s create two separate classes to represent regular and premiums users.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RegularUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SaltedPassword&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// No nullable CreditCard anymore&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PremiumUser&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BecomePremium&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Imagine we sent an email and validate credit card&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// details, etc&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PremiumUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PremiumUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SaltedPassword&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Do stuff of Premium Users...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChargeMonthlySubscription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// CreditCard is not null here.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we wrote two separate classes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegularUser&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PremiumUser&lt;/code&gt;. We don’t have methods that should be called only when some optional properties have value. And we don’t need to check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; anymore. For premium users, we’re sure we have their credit card details. We eliminated a possible source of NullReferenceException.&lt;/p&gt;

&lt;p&gt;We’re better off writing separate classes than writing a single large class with nullable properties that only have values at some point.&lt;/p&gt;

&lt;p&gt;I learned about this technique after reading &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Model Made Functional&lt;/a&gt;. The book uses the mantra: &lt;em&gt;“Make illegal state unrepresentable.”&lt;/em&gt; In our example, the illegal state is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; for regular users. We made it unrepresentable by writing two classes.&lt;/p&gt;

&lt;p&gt;Voilà! This is another technique to prevent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and NullReferenceException by avoiding classes that only use some optional state at some point of the object lifecycle. We should split all possible combinations of the optional state into separate classes. Put separate state in separate objects.&lt;/p&gt;

&lt;p&gt;Don’t miss the other posts in this series, &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;what the NullReferenceException is and how to prevent it&lt;/a&gt;, &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;Nullable Operators and References&lt;/a&gt;, and &lt;a href=&quot;/2023/03/20/UseOptionInsteadOfNull/&quot;&gt;the Option type and LINQ XOrDefault methods&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Join my course &lt;a href=&quot;https://www.udemy.com/course/csharp-nullreferenceexception-demystified/?referralCode=CC2A6F51EF27A75F1364&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;NRECourse-Footer&quot; data-goatcounter-title=&quot;NRE Course: Footer&quot;&gt;C# NullReferenceException Demystified&lt;/a&gt; on Udemy and learn the principles, features, and strategies to avoid this exception in just 1 hour and 5 minutes.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Goodbye, NullReferenceException: Option and LINQ</title>
   <link href="https://canro91.github.io/2023/03/20/UseOptionInsteadOfNull/"/>
   <updated>2023-03-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/03/20/UseOptionInsteadOfNull</id>
   <content type="html">&lt;p&gt;In the &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;previous post of this series&lt;/a&gt;, we covered three C# operators to simplify null checks and C# 8.0 Nullable References to signal when things can be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this post, let’s learn a more “functional” approach to removing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and how to use it to avoid &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; when working with LINQ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault&lt;/code&gt; methods.&lt;/p&gt;

&lt;h2 id=&quot;1-use-option-a-more-functional-approach-to-nulls&quot;&gt;1. Use Option: A More Functional Approach to Nulls&lt;/h2&gt;

&lt;p&gt;Functional languages like F# or Haskell use a different approach for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and optional values. Instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, they use an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; type, we have a “box” that might have a value or not. It’s the same concept of nullable ints, for example. I bet you have already used them. Let’s see an example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maybeAnInt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasValue&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maybeAnInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dangerousInt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maybeAnInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                            ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Nullable object must have a value.&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;safeInt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maybeAnInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetValueOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With nullable ints, we have variable that either holds an interger or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. They have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasValue&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Value&lt;/code&gt; properties, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetValueOrDefault()&lt;/code&gt; method to access their inner value.&lt;/p&gt;

&lt;p&gt;We can extend the concept of a box with possibly a value to reference types with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; type. We can wrap our reference types like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;int&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;Movie&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1618914241432-5043b1b4acf5?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4MDY1MDYwNQ&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;A set of gift boxes&quot; /&gt;

&lt;figcaption&gt;An Option is like a box. Photo by &lt;a href=&quot;https://unsplash.com/@sasun1990?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Sasun Bughdaryan&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/hpSf4EWNp7E?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;the-option-type&quot;&gt;The Option Type&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; type has two subtypes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Some&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Some&lt;/code&gt; represents a box with a value inside it, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;, an empty box.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; has two basic methods:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;One method to put something into a box. Often we call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt;. For this, we can use the constructor of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Some&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;One method to open the box, transform its value and return a new one with the result. Let’s call this method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Map&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-03-20-UseOptionInsteadOfNull/UnitAndMap.png&quot; alt=&quot;Option Unit and Map functions&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Option&apos;s Unit and Map functions&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Let’s use the &lt;a href=&quot;https://github.com/nlkl/Optional&quot;&gt;Optional library&lt;/a&gt;, &lt;em&gt;a robust option type for C#&lt;/em&gt;, to see how to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Some&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Map()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someInt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doubleOfSomeInt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValueOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 84&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doubleOfNone&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                       &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValueOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// -1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We created two optional ints: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;someInt&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;none&lt;/code&gt;. Then, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Map()&lt;/code&gt; to double their values. Then, to retrieve the value of each optional, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValueOr()&lt;/code&gt; with a default value.&lt;/p&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;someInt&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Map()&lt;/code&gt; returned another optional with the double of 42 and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValueOr()&lt;/code&gt; returned the same result. And for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;none&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Map()&lt;/code&gt; returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValueOr()&lt;/code&gt; returned -1.&lt;/p&gt;

&lt;h3 id=&quot;how-to-flatten-nested-options&quot;&gt;How to Flatten Nested Options&lt;/h3&gt;

&lt;p&gt;Now, let’s rewrite the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpContext&lt;/code&gt; example from previous posts,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                    ^^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that instead of appending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt; to type declarations like what we did with in the past post when we covered &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;C# 8.0 Nullable References&lt;/a&gt;, we wrapped them around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This time, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current&lt;/code&gt; is a box with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Request&lt;/code&gt; as another box inside. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Request&lt;/code&gt; has the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt; as another box.&lt;/p&gt;

&lt;p&gt;Now, let’s retrieve the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FlatMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FlatMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValueOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/some-default-path-here&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Or&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//.Match((path) =&amp;gt; path , () =&amp;gt; &quot;/some-default-path-here&quot;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This isn&apos;t the real HttpContext class...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We&apos;re writing some dummy declarations to prove a point&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To get the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt; value, we had to open all boxes that contain it. For that, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FlatMap()&lt;/code&gt; method. It grabs the value in the box, transforms it, and returns another box. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FlatMap()&lt;/code&gt;, we can flatten two nested boxes.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2023-03-20-UseOptionInsteadOfNull/FlatMap.png&quot; alt=&quot;Option FlatMap function&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Option&apos;s FlatMap to flatten nested options&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice we didn’t do any transformation with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FlatMap()&lt;/code&gt;. We only retrieved the inner value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt;, which was already another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is how we read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FlatMap()&lt;/code&gt;, we opened the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current&lt;/code&gt; box and grabbed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Request&lt;/code&gt; box in it.&lt;/li&gt;
  &lt;li&gt;Then, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FlatMap()&lt;/code&gt; again to open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Request&lt;/code&gt; and grab the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Finally, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValueOr()&lt;/code&gt;, we took out the value inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt; if it had any. Otherwise, if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt; was empty, it returned a default value of our choice.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;“This is the way!” Sorry, this is the “functional” way! We can think of nullable ints like ints being wrapped around a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Nullable&lt;/code&gt; box with more compact syntax and some helper methods.&lt;/p&gt;

&lt;h2 id=&quot;2-option-and-linq-xordefault-methods&quot;&gt;2. Option and LINQ XOrDefault methods&lt;/h2&gt;

&lt;p&gt;Another source of NullReferenceException is when we don’t check the result of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LastOrDefault&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;These methods return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; when the source collection has reference types, and there are no matching elements. In fact, this is one of the &lt;a href=&quot;/2022/06/13/LinqMistakes/&quot;&gt;most common mistakes when working with LINQ&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are some alternatives to prevent the NullReferenceException when working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;.NET 6.0 released some new LINQ methods and overloads&lt;/a&gt;. With .NET 6.0, we can use a second parameter with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault&lt;/code&gt; methods to pass a default value of our choice. Also, we can use &lt;a href=&quot;/2020/11/17/DefaultOrEmpty/&quot;&gt;the DefaultIfEmpty method&lt;/a&gt; instead of filtering collections with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;use-optionals-xornone&quot;&gt;Use Optional’s XOrNone&lt;/h3&gt;

&lt;p&gt;But, let’s combine the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault&lt;/code&gt; methods with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; type. We can make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault&lt;/code&gt; methods return an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Optional library has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrNone()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LastOrNone()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrNone()&lt;/code&gt; instead of the usual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;This time, let’s use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrNone()&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Optional.Collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//    ^^^^^&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Shrek&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3.95f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Inside Out&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2015&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.1f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Ratatouille&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Toy Story&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.1f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cloudy with a Chance of Meatballs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3.75f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theBestOfAll&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// With .NET FirstOrDefault()&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theBest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;theBestOfAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// With Optional&apos;s FirstOrNone()&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theBestAgain&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrNone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;//    ^^^^^&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ValueOr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theBestOfAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theBestAgain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrNone&lt;/code&gt; methods, we’re forced to check if they return something before trying to use their result.&lt;/p&gt;

&lt;p&gt;Voilà! That’s the functional way of doing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, with the Option or Maybe type. Here we used the Optional library, but there’s also another library I like: &lt;a href=&quot;https://github.com/atifaziz/Optuple&quot;&gt;Optuple&lt;/a&gt;. It uses the tuple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(bool HasValue, T Value)&lt;/code&gt; to represent the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Some&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt; subtypes.&lt;/p&gt;

&lt;p&gt;Even though we used a library to bring the Option type, we can implement our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; type and its methods. It’s not that difficult. We need an abstract class with two child classes and a couple of extension methods to make it work.&lt;/p&gt;

&lt;p&gt;Don’t miss the other posts in this series, &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;what the NullReferenceException is and when it’s thrown&lt;/a&gt;, &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;nullable operators and references&lt;/a&gt;, and &lt;a href=&quot;/2023/04/03/SeparateStateIntoSeparateObjects/&quot;&gt;separate optional state into separate objects&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Join my course &lt;a href=&quot;https://www.udemy.com/course/csharp-nullreferenceexception-demystified/?referralCode=CC2A6F51EF27A75F1364&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;NRECourse-Footer&quot; data-goatcounter-title=&quot;NRE Course: Footer&quot;&gt;C# NullReferenceException Demystified&lt;/a&gt; on Udemy and learn the principles, features, and strategies to avoid this exception in just 1 hour and 5 minutes.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Goodbye, NullReferenceException: Nullable Operators and References</title>
   <link href="https://canro91.github.io/2023/03/06/NullableOperatorsAndReferences/"/>
   <updated>2023-03-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/03/06/NullableOperatorsAndReferences</id>
   <content type="html">&lt;p&gt;In the &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;previous post of this series&lt;/a&gt;, we covered two ideas to avoid the NullReferenceException: we should check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; before accessing the members of an object and check the input parameters of our methods.&lt;/p&gt;

&lt;p&gt;Let’s see some new C# operators to simplify null checking and a new feature to better signal possible &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; references.&lt;/p&gt;

&lt;h2 id=&quot;1-c-nullable-operators&quot;&gt;1. C# Nullable Operators&lt;/h2&gt;

&lt;p&gt;C# has three operators to simplify our null checks: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?.&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??=&lt;/code&gt;. These operators don’t prevent us from having &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; in the first place, but they help us to easily write our null checks.&lt;/p&gt;

&lt;h3 id=&quot;without-nullable-operators&quot;&gt;Without Nullable Operators&lt;/h3&gt;

&lt;p&gt;Let’s start with an example and refactor it to use these new operators.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/some-default-path-here&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you have worked with the &lt;a href=&quot;/2020/03/23/GuideToNetCore/&quot;&gt;old ASP.NET framework&lt;/a&gt;, you might have done something like that. If not, don’t worry. We’re only accessing a property down in a property chain, but any of those properties could be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Notice that to defend against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, we checked if every property was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; to “fail fast” and use a default value.&lt;/p&gt;

&lt;h3 id=&quot;with-nullable-operators&quot;&gt;With Nullable Operators&lt;/h3&gt;

&lt;p&gt;Now, let’s use the new nullable operators instead,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                           ^^^      ^^^&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/some-default-path-here&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//           ^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;More compact, right?&lt;/p&gt;

&lt;p&gt;With the &lt;strong&gt;null-conditional operator&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?.&lt;/code&gt;), we access the property or method of an object only if the object isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. Otherwise, the entire expression evaluates to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. For our example, we retrieve &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt; only if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Request&lt;/code&gt; isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current&lt;/code&gt; isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpContext&lt;/code&gt; isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, with the &lt;strong&gt;null-coalescing operator&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt;), we evaluate an alternative expression if the one on the left of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt; operator isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. For our example, if any of the properties in the chain to retrieve &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, the whole expression is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path&lt;/code&gt; variable gets assigned to the default string.&lt;/p&gt;

&lt;p&gt;With the &lt;strong&gt;null-coalescing assignment operator&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??=&lt;/code&gt;), we assign a new value to a variable only if it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. We could also write our example like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/some-default-path-here&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  ^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The same as&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//if (path == null)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//{&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//    path = &quot;/some-default-path-here&quot;; &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how we refactored our original example to only two lines of code with these three new operators. Again we could still have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; values. These nullable operators make our lives easier by simplifying our null checks.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1517420704952-d9f39e95b43e?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY4MDY0OTcyNw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Electronic devices in a workbench&quot; /&gt;

&lt;figcaption&gt;What if we could tell when something is null? Photo by &lt;a href=&quot;https://unsplash.com/@nicolasthomas?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Nicolas Thomas&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/3GZi6OpSDcY?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-c-nullable-references&quot;&gt;2. C# Nullable References&lt;/h2&gt;

&lt;p&gt;To solve the NullReferenceException, we should check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. We got that! But the thing is knowing when we should do it or not. That’s precisely what C# 8.0 solves with Nullable References.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With C# 8.0, all reference variables are non-nullable by default. Accessing the member of nullable references results in compiler warnings or errors.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a breaking change. Therefore we need to turn on this feature at the project level in our csproj files. Like these,&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net8.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--             ^^^^^^ --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- We could use netcoreapp3.1|net5.0|net6.0|.net7.0 too --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--      ^^^^^^ --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- We could use enable|disable|warning too --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;WarningsAsErrors&amp;gt;&lt;/span&gt;nullable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/WarningsAsErrors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--              ^^^^^^^^ --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- We can take the extreme route --&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To use Nullable References, we need to target .NET Core 3 and upward. And inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Nullable&lt;/code&gt; node in our csproj files, we could use: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warning&lt;/code&gt;. Even, we can take the extreme route and consider all nullable warnings as compilation errors.&lt;/p&gt;

&lt;h3 id=&quot;with-nullable-references-on&quot;&gt;With Nullable References On&lt;/h3&gt;

&lt;p&gt;Let’s see what our motivating example looks like with Nullable References turned on,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//            ^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// CS8600: Converting null literal or possible null value to non-nullable type&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/some-default-path-here&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This isn&apos;t the real HttpContext class...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We&apos;re writing some dummy declarations to prove a point&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                        ^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// CS8618: Non-nullable field &apos;Current&apos; must contain&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// a non-nullable value when exiting constructor&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// CS8618: Non-nullable field &apos;Current&apos; must contain&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// a non-nullable value when exiting constructor&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we have a warning when initializing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. And another one in the declaration of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpContext&lt;/code&gt; class if we don’t initialize any non-nullable fields. That’s not the real &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpContext&lt;/code&gt; by the way but bear with me. Also, we don’t need to check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; when retrieving the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationPath&lt;/code&gt; since all our references aren’t nullable by definition.&lt;/p&gt;

&lt;p&gt;To declare a variable that can be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, we need to add to its type declaration a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt;. In the same way, we have always declared nullable primitive types like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int?&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;without-null-checks&quot;&gt;Without Null Checks&lt;/h3&gt;

&lt;p&gt;Let’s change our example to have nullable references and no null checks,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Notice the ? symbol here&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//   vvv&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//             ^^^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// CS8602: Deference of a possibly null value&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This isn&apos;t the real HttpContext class...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We&apos;re writing some dummy declarations to prove a point&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HttpContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                      ^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Notice the ? symbol here&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// No more warnings here&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplicationPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice this time, when declaring the variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path&lt;/code&gt;, we have a warning because we’re accessing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current&lt;/code&gt; property, which might be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. Also, notice we changed, inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpContext&lt;/code&gt; class, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current&lt;/code&gt; property to have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpContext?&lt;/code&gt; type (with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt; at the end of the type).&lt;/p&gt;

&lt;p&gt;Now with Nullable References, we have a way of telling when we should check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; by looking at the signature of our methods.&lt;/p&gt;

&lt;p&gt;Voilà! Those are the new C# operators to simplify our null checks. We said “new” operators, but we have had them since C# 6.0. And that’s how we can tell if our references can be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; or not using Nullable References.&lt;/p&gt;

&lt;p&gt;We have these nullable operators available even if we’re using the old .NET Framework. But, to use Nullable References, we should upgrade at least to .NET Core 3.0.&lt;/p&gt;

&lt;p&gt;In the next post, we will cover &lt;a href=&quot;/2023/03/20/UseOptionInsteadOfNull/&quot;&gt;the Option type as an alternative to null&lt;/a&gt; and how to avoid the NullReferenceException when working with LINQ.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Join my course &lt;a href=&quot;https://www.udemy.com/course/csharp-nullreferenceexception-demystified/?referralCode=CC2A6F51EF27A75F1364&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;NRECourse-Footer&quot; data-goatcounter-title=&quot;NRE Course: Footer&quot;&gt;C# NullReferenceException Demystified&lt;/a&gt; on Udemy and learn the principles, features, and strategies to avoid this exception in just 1 hour and 5 minutes.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Goodbye, NullReferenceException: What it is and how to avoid it</title>
   <link href="https://canro91.github.io/2023/02/20/WhatNullReferenceExceptionIs/"/>
   <updated>2023-02-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/02/20/WhatNullReferenceExceptionIs</id>
   <content type="html">&lt;p&gt;If you’re here, I bet you already have found the exception message: &lt;em&gt;“Object reference not set to an instance of an object.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this series of posts, let’s see some techniques to completely eliminate the NullReferenceException from our code. Let’s start by understanding when NullReferenceException is thrown and a strategy to fix it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NullReferenceException is thrown when we access a property or method of an uninitialized variable of a reference type. The easier way to solve this exception is to check for null before accessing the members of an object. But C# has introduced new operators and features to avoid this exception.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s write an example that throws NullReferenceException,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// System.NullReferenceException: &apos;Object reference not set to an instance of an object.&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// movie was null.&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Imagine this is a database call that might&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// or might not return a movie&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FindMovie()&lt;/code&gt;. That caused the NullReferenceException. But, it could be a method that accessed a database and didn’t find anything or an API controller method that didn’t receive a required input parameter.&lt;/p&gt;

&lt;p&gt;In our last example, we got a NullReferenceException when we returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; from a method. But, we could also find this exception when we try to loop through a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; list or array, for example.&lt;/p&gt;

&lt;p&gt;Speaking of returning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, &lt;strong&gt;one way to prevent the NullReferenceException is to never pass null between objects&lt;/strong&gt;. Instead of returning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, let’s use empty lists and strings, or the Null Object pattern. And let’s &lt;a href=&quot;/2021/09/27/TwoCSharpIdiomsPart4/&quot;&gt;use intention-revealing defaults&lt;/a&gt; for that.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1570645053711-5767083d2518?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY3Njc3NjY0Nw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Multicolored dream catcher&quot; /&gt;

&lt;figcaption&gt;Don&apos;t catch it! Photo by &lt;a href=&quot;https://unsplash.com/@drderp94?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;David Restrepo Parrales&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/zgeYcpeussI?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;1-dont-catch-the-nullreferenceexception&quot;&gt;1. Don’t catch the NullReferenceException&lt;/h2&gt;

&lt;p&gt;To fix the NullReferenceException, we might be tempted to write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;try/catch&lt;/code&gt; block around the code that throws it. But, let’s not catch the NullReferenceException.&lt;/p&gt;

&lt;p&gt;Let me say that again: &lt;strong&gt;don’t throw or catch NullReferenceException&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By any means, please, let’s not write something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;AMethodThatMightThrowNullReferenceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullReferenceException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Doing something with the exception here&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A NullReferenceException is a symptom of an unhandled and unexpected scenario in our code, and catching it won’t handle that. A NullReferenceException is a developer’s mistake.&lt;/p&gt;

&lt;h2 id=&quot;2-check-for-null&quot;&gt;2. Check for null&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The solution for the NullReferenceException is to check for nulls and defense against them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s fix our previous example by adding a null check. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We&apos;re safe here...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// No more System.NullReferenceException&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// at least due to a movie being null &lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FindMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Imagine this is a database call that might&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// or might not return a movie&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we checked if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movie&lt;/code&gt; variable wasn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. As simple as that.&lt;/p&gt;

&lt;h3 id=&quot;alternatives-to-check-for-null&quot;&gt;Alternatives to check for null&lt;/h3&gt;

&lt;p&gt;The lack of options isn’t an excuse to not check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. Since &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;some recent C# versions&lt;/a&gt;, and thanks to pattern matching, we have a couple of new ways to check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. We can use any of these:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (movie != null) { //... }&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (movie is not null) { //... }&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (movie is { }) { //... }&lt;/code&gt;, and,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (movie is object) { //... }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of those don’t look like C# anymore. I still prefer the old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (movie != null) ...&lt;/code&gt;. Which one do you prefer?&lt;/p&gt;

&lt;h2 id=&quot;3-defense-against-null&quot;&gt;3. Defense against null&lt;/h2&gt;

&lt;p&gt;If the solution to NullReferenceException were to simply check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, that wouldn’t be the Billion-Dollar mistake. And I wouldn’t be writing this series.&lt;/p&gt;

&lt;p&gt;But the thing is knowing when a reference might be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; or not and, therefore, when we should check for it. That’s when we should protect ourselves against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To protect against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, inside our methods, let’s check our input parameters and throw a more detailed exception. C# has an exception for missing parameters: ArgumentNullException.&lt;/p&gt;

&lt;p&gt;The ArgumentNullException stack trace contains the name of the parameter that was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. That will help us to better troubleshoot our code. Often, the NullReferenceException stack trace doesn’t include what was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; in the first place. Happy debugging time!&lt;/p&gt;

&lt;p&gt;Let’s check our input parameters,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Since C# 10, we can also write:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ArgumentNullException.ThrowIfNull(movie);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Doing something here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that instead of waiting for NullReferenceException, we proactively prevented it by checking for a required parameter and throwing a controlled ArgumentNullException.&lt;/p&gt;

&lt;p&gt;Voilà! That’s the NullReferenceException and how to fix it by checking for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. Remember, we shouldn’t catch this exception but prevent and prepare for it.&lt;/p&gt;

&lt;p&gt;Don’t miss the other posts in this series! In the next post, we cover how to use &lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;Nullable Operators and Nullable References&lt;/a&gt; to prevent the NullReferenceException. In future posts, we’re covering &lt;a href=&quot;/2024/11/14/NoNulIsAGoodIdea/&quot;&gt;why getting rid of null is a good idea&lt;/a&gt;, &lt;a href=&quot;/2023/03/20/UseOptionInsteadOfNull/&quot;&gt;the Option type and LINQ XOrDefault methods&lt;/a&gt;, and &lt;a href=&quot;/2023/04/03/SeparateStateIntoSeparateObjects/&quot;&gt;a design technique to encapsulate optional state&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Join my course &lt;a href=&quot;https://www.udemy.com/course/csharp-nullreferenceexception-demystified/?referralCode=CC2A6F51EF27A75F1364&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;NRECourse-Footer&quot; data-goatcounter-title=&quot;NRE Course: Footer&quot;&gt;C# NullReferenceException Demystified&lt;/a&gt; on Udemy and learn the principles, features, and strategies to avoid this exception in just 1 hour and 5 minutes.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Monday Links: Passions, Estimates, and Methodologies</title>
   <link href="https://canro91.github.io/2023/01/23/MondayLinks/"/>
   <updated>2023-01-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/01/23/MondayLinks</id>
   <content type="html">&lt;p&gt;Welcome to the first Monday Links of 2023. These are five reads I found interesting last month. This time, I found software methodologies was a recurring theme these days.&lt;/p&gt;

&lt;h2 id=&quot;why-im-glad-i-lack-passion-to-be-a-programmer&quot;&gt;Why I’m Glad I Lack Passion to BE a Programmer&lt;/h2&gt;

&lt;p&gt;After a couple of years of working as a software engineer, I started to embrace simplicity. Software exists to satisfy a business need. Perfect software only exists in books. That’s why I started to see the big goal and only use libraries/tools/concepts when there’s a compelling reason to do so. Not all applications need to use Domain-Driven Design with Event Sourcing.&lt;/p&gt;

&lt;p&gt;I liked this one: &lt;em&gt;My ideal for software development is to find the simplest solution to the practical problem.&lt;/em&gt; I’m not a passionate programmer anymore either.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.miris.design/not-a-programmer&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;11-laws-of-software-estimation-for-complex-work&quot;&gt;11 Laws of Software Estimation for Complex Work&lt;/h2&gt;

&lt;p&gt;I can relate to this story. It happens to a friend of a friend of mine. One day his CEO came saying he just closed a really big deal but he didn’t know what they were going to do. Arrrggg!&lt;/p&gt;

&lt;p&gt;Apart from a relatable story, this post contains 11 rules about estimations. Like all estimations are simply guesses and by the time developers have enough information to give more accurate estimations, close to the end of a project, it’s already too late.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mdalmijn.com/p/11-laws-of-software-estimation-for-complex-work&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;user-interface-design-5-rules-of-thumb&quot;&gt;User Interface Design: 5 Rules of Thumb&lt;/h2&gt;

&lt;p&gt;This article contains some basic principles to design good UI. I don’t like those “are you sure you want to do something?” messages. It’s not a good idea based on this article.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mannhowie.com/ui-design-web&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-do-many-developers-consider-scrum-to-be-an-evil-scam&quot;&gt;Why Do Many Developers Consider Scrum to Be an Evil Scam?&lt;/h2&gt;

&lt;p&gt;Like any widespread idea, it gets perverted over time. I’ve been in teams where SCRUM is only adopted to micromanage developers using daily meetings. And the next thing you know is that daily meetings become a narrative of how busy developers are to avoid getting fired.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/serious-scrum/why-do-many-developers-consider-scrum-to-be-an-evil-scam-4b9ee70c5b0b&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-dont-software-development-methodologies-work&quot;&gt;Why don’t software development methodologies work?&lt;/h2&gt;

&lt;p&gt;This is an old article I found on Hacker News front page. It showed, years ago, what everybody is complaining about these days. You only need to visit Hacker News or r/programming once every month.&lt;/p&gt;

&lt;p&gt;I’ve been in small projects with no methodologies to larger projects with SCRUM as religion. I’ve been there.&lt;/p&gt;

&lt;p&gt;I like this paragraph from the article: &lt;em&gt;“My own experience, validated by Cockburn’s thesis and Frederick Brooks in No Silver Bullet, is that software development projects succeed when the key people on the team share a common vision, what Brooks calls ‘conceptual integrity.’“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Apart from the main article, it has really good comments. This is one that resonates with me about the one methodology:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“A single technical lead with full authority to make decisions, with a next tier assistant, associated technical staff, and a non-technical support person. the achievement of the team is then determined by the leadership of the team. the size of the team and project complexity is then limited by the leader and her ability to understand the problem and assign tasks.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For me, the most successful project are the ones with a small team who knows each other, and everyone knows the main goal and what to do.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://typicalprogrammer.com/why-dont-software-development-methodologies-work&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links. Do you consider yourself a passionate developer? What’s your experience with software methodologies?&lt;/p&gt;

&lt;p&gt;In the meantime, don’t miss the previous &lt;a href=&quot;/2022/11/21/MondayLinks/&quot;&gt;Monday Links on 40-year Programmer, Work and Burnout&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Best of 2022</title>
   <link href="https://canro91.github.io/2023/01/09/BestOf2022/"/>
   <updated>2023-01-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2023/01/09/BestOf2022</id>
   <content type="html">&lt;p&gt;In 2022, I wrote two major series of posts: one about SQL Server performance tuning and another one about LINQ.&lt;/p&gt;

&lt;p&gt;Once one of my clients asked me to “tune” some stored procedures and that inspired me to take a close look at the performance tuning world. Last year, I took &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar Mastering courses&lt;/a&gt; and decided to share some of the things I learned.&lt;/p&gt;

&lt;p&gt;On another hand, I updated my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;Quick Guide to LINQ&lt;/a&gt; to use new C# features and wrote a bunch of new posts about LINQ. In fact, I released one text-based course about LINQ on Educative: &lt;a href=&quot;https://www.educative.io/courses/getting-started-linq-c-sharp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Getting Started with LINQ&lt;/a&gt;. That’s my favorite C# feature, ever.&lt;/p&gt;

&lt;p&gt;I kept writing my &lt;a href=&quot;/tags/mondaylinks&quot;&gt;Monday Links posts&lt;/a&gt;. And, I decided to have my own Advent of Code. I prefer to call it: &lt;a href=&quot;/AdventOfCode2022&quot;&gt;Advent of Posts&lt;/a&gt;. I wrote 22 posts in December. One post per day until Christmas eve. I missed a couple of days. But I consider it a “mission accomplished.”&lt;/p&gt;

&lt;p&gt;These are the 5 posts I wrote in 2022 you read the most. In case you missed any of them, here they are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/03/07/OptimizeGroupBySQLServer/&quot;&gt;TIL: How to optimize Group by queries in SQL Server&lt;/a&gt;. This post has one of the lessons I learned after following Brent Ozar’s Mastering courses. Well, this one is about using CTEs to speed up queries with GROUP BY.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;SQL Server Index recommendations: Just listen to them&lt;/a&gt;. Again, these are some of the lessons I learned in Brent Ozar’s Mastering Index Tuning course. I shared why we shouldn’t blindly create indexes recommendations from query plans. They’re only clues. We can do better than that. I have to confess I added every single index recommendation I got before learning these lessons.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/04/11/DistributedCacheWithNCache/&quot;&gt;Working with ASP.NET Core IDistributedCache Provider for NCache&lt;/a&gt;. This is a post I wrote in collaboration with Alachisoft, NCache creators. It introduces NCache and shows how to use it as a DistributedCache provider with ASP.NET Core.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;Four new LINQ methods in .NET 6: Chunk, DistinctBy, Take, XOrDefault&lt;/a&gt;. After many years, with the .NET 6 release, LINQ received new methods and overloads. In this post, I show some of them.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/05/30/HowToUseLinqGroupBy/&quot;&gt;How to use LINQ GroupBy method?&lt;/a&gt; Another post to get started with LINQ. It covers three GroupBy use cases.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voilà! These were your 5 favorite posts. Hope you enjoy them as much as I did writing them. Probably, you found shorter versions of these posts on my &lt;a href=&quot;https://dev.to/canro91&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dev.to account&lt;/a&gt;. Or the version of some random guy copy-pasted into his own website pretending I was an author on his programming site. Things you find out when you google your own user handle. Arrggg!&lt;/p&gt;

&lt;p&gt;If any of my posts have helped you and you want to support my work, &lt;a href=&quot;https://www.educative.io/courses/getting-started-linq-c-sharp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;check my Getting Started with LINQ course on Educative&lt;/a&gt; or buy me a coffee, tea, or hamburger by &lt;a href=&quot;https://imcsarag.gumroad.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;downloading my ebooks from my Gumroad page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don’t miss my &lt;a href=&quot;/2022/01/10/BestOf2021/&quot;&gt;best of 2021&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading, and happy coding in 2023!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Let&apos;s refactor a test: Remove duplicated emails</title>
   <link href="https://canro91.github.io/2022/12/22/TestingDuplicatedEmails/"/>
   <updated>2022-12-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/22/TestingDuplicatedEmails</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently, I’ve been &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;reviewing pull requests&lt;/a&gt; as one of my main activities. This time, let’s refactor two tests I found on one code review session. The two tests check if an email doesn’t have duplicated addresses before sending it. But, they have a common mistake: testing private methods directly. Let’s refactor these tests to use the public facade of methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always write unit tests using the public methods of a class or a group of classes. Don’t make private methods public and static to test them directly. Test the observable behavior of classes instead.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;here-are-the-test-to-refactor&quot;&gt;Here are the test to refactor&lt;/h2&gt;

&lt;p&gt;These tests belong to an email component in a Property Management Solution. This component stores all emails before sending them.&lt;/p&gt;

&lt;p&gt;These are two tests to check we don’t try to send an email to the same addresses. Let’s pay attention to the class name and method under test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SendEmailCommandHandlerTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateRecipients_NoDuplicates_ReturnsSameRecipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toEmailAddresses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;toMail1@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;toMail2@mail.com&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccEmailAddresses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;ccMail3@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ccMail4@mail.com&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recipients&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendEmailCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateRecipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toEmailAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccEmailAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                       ^^^^^&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeEquivalentTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;toMail1@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;toMail2@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ccMail3@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ccMail4@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateRecipients_Duplicates_ReturnsRecipientsWithoutDuplicates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toEmailAddresses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;toMail1@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;toMail2@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;toMail1@mail.com&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccEmailAddresses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;ccMail1@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;toMail2@mail.com&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recipients&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendEmailCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateRecipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toEmailAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccEmailAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                       ^^^^^&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BeEquivalentTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;toMail1@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;toMail2@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ccMail1@mail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I slightly changed some names. But those are the real tests I had to refactor.&lt;/p&gt;

&lt;p&gt;What’s wrong with those tests? Did you notice it? Also, can you point out where the duplicates are in the second test?&lt;/p&gt;

&lt;p&gt;To have more context, here’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendEmailCommandHandler&lt;/code&gt; class that contains the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateRecipients()&lt;/code&gt; method,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MediatR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyCoolProject.Commands&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyCoolProject.Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyCoolProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SendEmailCommandHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IRequestHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendEmailCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrackingId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEmailRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendEmailCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateDispatchCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IEmailRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateDispatchCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrackingId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendEmailCommand&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Imagine some validations and initializations here...&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recipients&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateRecipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ccs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;recipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_emailRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrackingId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateRecipients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                                   ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnionBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ccs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recipient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EmailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RecipientType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RecipientType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RecipientType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RecipientType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RecipientType&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;To&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cc&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendEmailCommandHandler&lt;/code&gt; processes all requests to send an email. It grabs the input parameters, creates an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Email&lt;/code&gt; class, and stores it using a repository. It uses the &lt;a href=&quot;https://github.com/jbogard/MediatR&quot;&gt;free MediatR library&lt;/a&gt; to roll commands and command handlers.&lt;/p&gt;

&lt;p&gt;Also, it parses the raw email addresses into a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Recipient&lt;/code&gt; with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateRecipients()&lt;/code&gt; method. That’s the method under test in our two tests. Here the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Recipient&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmailAddress&lt;/code&gt; work like &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;Value Objects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now can you notice what’s wrong with our tests?&lt;/p&gt;

&lt;h2 id=&quot;whats-wrong&quot;&gt;What’s wrong?&lt;/h2&gt;

&lt;p&gt;Our two unit tests test a private method directly. That’s not the appropriate way of writing unit tests. We shouldn’t test internal state and private methods. We should test them through the public facade of our logic under test.&lt;/p&gt;

&lt;p&gt;In fact, someone made the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateRecipients()&lt;/code&gt; method public to test it,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-12-22-TestingDuplicatedEmails/Diff.png&quot; alt=&quot;Diff showing a private method made public&quot; /&gt;
    &lt;figcaption&gt;Someone made the internals public to write tests&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Making private methods public to test them is &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;the most common mistake on unit testing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For our case, we should write our tests using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendEmailCommand&lt;/code&gt; class and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Handle()&lt;/code&gt; method.&lt;/p&gt;

&lt;h2 id=&quot;dont-expose-private-methods&quot;&gt;Don’t expose private methods&lt;/h2&gt;

&lt;p&gt;Let’s make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateRecipients()&lt;/code&gt; private again. And let’s write our tests using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendEmailCommand&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendEmailCommandHandler&lt;/code&gt; classes.&lt;/p&gt;

&lt;p&gt;This is the test to validate that we remove duplicates,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Handle_DuplicatedEmailInTosAndCc_CallsRepositoryWithoutDuplicates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duplicated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;duplicated@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duplicated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tomail@mail.com&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duplicated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ccmail@mail.com&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDispatchRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateDispatchCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SendEmailCommandHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;());&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Let&apos;s write a factory method that receives these two email lists&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Let&apos;s write some assert/verifications in terms of the Email object&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fakeRepository&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* Assert something here using Recipients */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;());&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Or, even better let&apos;s write a custom Verify()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// fakeRepository.WasCalledWithoutDuplicates();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SendEmailCommand&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ccs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SendEmailCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Any Subject&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Any Body&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ccs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildCommand()&lt;/code&gt; method to create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SendEmailCommand&lt;/code&gt; only with the email addresses. That’s what we care about in this test. This way we &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;reduce the noise in our tests&lt;/a&gt;. And, to make our test values obvious, we declared a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;duplicated&lt;/code&gt; variable and used it in both destination email addresses.&lt;/p&gt;

&lt;p&gt;To write the Assert part of this test, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; method from the fake repository to check that we have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;duplicated&lt;/code&gt; email only once. Or we can use the Moq &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Callback()&lt;/code&gt; method to capture the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Email&lt;/code&gt; being saved and write some assertions. Even better, we can create a &lt;a href=&quot;/2021/08/16/WriteCustomAssertions/&quot;&gt;custom assertion&lt;/a&gt; for that. Maybe, we can write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WasCalledWithoutDuplicates()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;That’s one of the two original tests. The other one is left as an exercise to the reader.&lt;/p&gt;

&lt;p&gt;Voilà! That was today’s refactoring session. To take home, we shouldn’t test private methods and always write tests using the public methods of the code under test. We can remember this principle with the mnemonic: “Don’t let others touch our private parts.” That’s how I remember it.&lt;/p&gt;

&lt;p&gt;For more refactoring sessions, check these two: &lt;a href=&quot;/2022/12/08/TestingOAuthConnections/&quot;&gt;store and update OAuth connections&lt;/a&gt; and &lt;a href=&quot;/2021/08/02/LetsRefactorATest/&quot;&gt;generate payment reports&lt;/a&gt;. Don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover from naming conventions to best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>To Value Object or Not To: How I choose Value Objects</title>
   <link href="https://canro91.github.io/2022/12/21/WhenToChooseValueObjects/"/>
   <updated>2022-12-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/21/WhenToChooseValueObjects</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today I &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;reviewed a pull request&lt;/a&gt; and had a conversation about when to use Value Objects instead of primitive values. This is the code that started the conversation and my rationale to promote a primitive value to a Value Object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer Value Objects to encapsulate validations or custom methods on a primitive value. Otherwise, if a primitive value doesn’t have a meaningful “business” sense and is only passed around, consider using the primitive value with a good name for simplicity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In case you’re not familiar with Domain-Driven Design and its artifacts. A Value Object represents a concept that doesn’t have an “identifier” in a business domain. Value objects are immutable and compared by value.&lt;/p&gt;

&lt;p&gt;Value Objects represent elements of “broader” concepts. For example, in a Reservation Management System, we can use a Value Object to represent the payment method of a Reservation.&lt;/p&gt;

&lt;h2 id=&quot;timestamp-vs-datetime&quot;&gt;TimeStamp vs DateTime&lt;/h2&gt;

&lt;p&gt;This is the piece of code that triggered my comment during the code review.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DeliveryNotification&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^^^&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEqualityComponents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TimeStamp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TimeStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TimeStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SystemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEqualityComponents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Sent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Opened&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Failed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wanted to record when an email is sent, opened, and clicked. We relied on a third-party Email Provider to notify our system about these email events. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeliveryNotification&lt;/code&gt; has an email address, status, and timestamp.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValueObject&lt;/code&gt; base class is &lt;a href=&quot;https://enterprisecraftsmanship.com/posts/value-object-better-implementation/&quot;&gt;Vladimir Khorikov’s ValueObject implementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeStamp&lt;/code&gt; class. It’s only a wrapper around the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; class. Mmmm…&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1578923931302-7fd9b3495be7?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY3MTQ5OTI3Mw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Sand clock&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@alexandar_todov?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Alexandar Todov&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/timer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;promote-primitive-values-to-value-objects&quot;&gt;Promote Primitive Values to Value Objects&lt;/h2&gt;

&lt;p&gt;I’d dare to say that using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeStamp&lt;/code&gt; instead of a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeliveryNotification&lt;/code&gt; class was an overkill. I guess when “when we have a hammer, everything looks like a finger.”&lt;/p&gt;

&lt;p&gt;This is my rationale to choose between value objects and primitive values:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If we need to enforce a domain rule or perform a business operation on a primitive value, let’s use a Value Object.&lt;/li&gt;
  &lt;li&gt;If we only pass a primitive value around and it represents a concept in the language domain, let’s wrap it around a record to give it a meaningful name.&lt;/li&gt;
  &lt;li&gt;Otherwise, let’s stick to the plain primitive values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeStamp&lt;/code&gt; class, apart from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create()&lt;/code&gt;, we didn’t have any other methods. We might validate if the inner date is in this century. But that won’t be a problem. I don’t think that code will live that long.&lt;/p&gt;

&lt;p&gt;And, there are cleaner ways of &lt;a href=&quot;/2021/05/10/WriteTestsThatUseDateTimeNow/&quot;&gt;writing tests that use DateTime&lt;/a&gt; than using a static &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SystemClock&lt;/code&gt;. Maybe, it would be a better idea if we can overwrite the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SystemClock&lt;/code&gt; internal date.&lt;/p&gt;

&lt;p&gt;I’d take a simpler route and use a plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; value. I don’t think there’s a business case for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeStamp&lt;/code&gt; here.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DeliveryNotification&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ValueObject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//     ^^^^^^&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetEqualityComponents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Recipient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Or alternative, to use the same domain language&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// public record TimeStamp(DateTime Value);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeliveryStatus&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Sent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Opened&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Failed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If in the “email sending” domain, business analysts or stakeholders use “timestamp,” for the sake of a ubiquitous language, we can add a simple record &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TimeStamp&lt;/code&gt; to wrap the date. Like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;record TimeStamp(DateTime value)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! That’s a practical option to decide when to use Value Objects and primitive values. For me, the key is asking if there’s a meaningful domain concept behind the primitive value. Otherwise we would end up with too many value objects or &lt;a href=&quot;/2020/12/10/PrimitiveObsession/&quot;&gt;obsessed with primitive values&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to read more about Domain-Driven Design, check my takeaways from these books &lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt; Hands-on Domain-Driven Design with .NET Core&lt;/a&gt; and &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dump and Load to squash old migrations</title>
   <link href="https://canro91.github.io/2022/12/20/SquashOldMigrations/"/>
   <updated>2022-12-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/20/SquashOldMigrations</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently, I stumbled upon the article &lt;a href=&quot;https://andrealeopardi.com/posts/get-rid-of-your-old-database-migrations/&quot;&gt;Get Rid of Your Old Database Migrations&lt;/a&gt;. The author shows how Clojure, Ruby, and Django use the “Dump and Load” approach to compact or squash old migrations. This is how I implemented the “Dump and Load” approach in one of my client’s projects.&lt;/p&gt;

&lt;h2 id=&quot;1-export-database-objects-and-reference-data-with-schemazen&quot;&gt;1. Export database objects and reference data with schemazen&lt;/h2&gt;

&lt;p&gt;In one of my client’s projects, we had too many migration files that we started to group them inside folders named after the year and month. Squashing migrations sounds like a good idea here.&lt;/p&gt;

&lt;p&gt;For example, for a three-month project, we wrote 27 migration files. This is the Migrator project,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-12-20-SquashOldMigrations/TooManyMigrations.png&quot; alt=&quot;List of migration files in one of my projects&quot; width=&quot;200px&quot; /&gt;
    &lt;figcaption&gt;27 migration files for a short-term project&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For those projects, we use &lt;a href=&quot;/2020/08/15/Simple.Migrations/&quot;&gt;Simple.Migrations to apply migration files&lt;/a&gt; and a bunch of custom C# extension methods to write the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Up()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Down()&lt;/code&gt; steps. Since we don’t use an all-batteries-included migration framework, I needed to generate the dump of all database objects.&lt;/p&gt;

&lt;p&gt;I found &lt;a href=&quot;https://github.com/sethreno/schemazen&quot;&gt;schemazen&lt;/a&gt; in GitHub, a CLI tool to &lt;em&gt;“script and create SQL Server objects quickly.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is how to script all objects and export data from reference tables with schemazen,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet schemazen script &lt;span class=&quot;nt&quot;&gt;--server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;localdb&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;MSSQLLocalDB
    &lt;span class=&quot;nt&quot;&gt;--database&lt;/span&gt; &amp;lt;YourDatabaseName&amp;gt;
    &lt;span class=&quot;nt&quot;&gt;--dataTablesPattern&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)(&lt;/span&gt;Status|Type&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;
    --scriptDir C:/someDir
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--dataTablesPattern&lt;/code&gt; option with a regular expression to only export the data from the reference tables. In this project, we named our reference tables with the suffixes “Status” or “Type.” For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PostStatus&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReceiptType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I could simply export the objects from SQL Server directly. But those script files contain a lot of noise in the form of default options. Schemazen does it cleanly.&lt;/p&gt;

&lt;p&gt;Schemazen generates one folder per object type and one file per object. And it exports data in a TSV format. I didn’t find an option to export the INSERT statements in its source code, though.&lt;/p&gt;

&lt;p&gt;Schemazen generates a folder structure like this,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; |-data
 |-defaults
 |-foreign_keys
 |-tables
 props.sql
 schemas.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this first step, I had the database objects. But I still needed to write the actual migration file.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1592915883536-d44208ad2baf?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY3MTUwMDEzOA&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Piles of used cars and trucks waiting to be recycled&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@randylaybourne?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Randy Laybourne&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-process-schemazen-exported-files&quot;&gt;2. Process schemazen exported files&lt;/h2&gt;

&lt;p&gt;To write the squash migration file, I wanted to have all scripts in a single file and turn the TSV files with the exported data into INSERT statements.&lt;/p&gt;

&lt;p&gt;I could write a C# script file, but I wanted to stretch my Bash/Unix muscles. After some Googling, I came up with this,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# It grabs the output from schemazen and compacts all dump files into a single one&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dump.sql

&lt;span class=&quot;c&quot;&gt;# Merge all files into a single one&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;folder &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;tables/&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;defaults/&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;foreign_keys/&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
    &lt;/span&gt;find &lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;*.sql&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;VersionInfo.sql&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\)&lt;/span&gt; | &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;f &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;do
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;done
done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Remove GO keywords and blank lines&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/^GO/d&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/^$/d&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Turn tsv files into INSERT statements&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;file &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;data/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;tsv&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;INSERT INTO &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;(Id, Name) VALUES&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;s/data&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\/&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;//&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;s/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tsv//&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print &quot;(&quot;$1&quot;,\047&quot;$2&quot;\047),&quot;}&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;
    
    &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/^$/d&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;$ s/,$//g&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first part merges all separate object files into a single one. I filtered the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VersionInfo&lt;/code&gt; table. That’s Simple.Migration’s table to keep track of already applied migrations.&lt;/p&gt;

&lt;p&gt;The second part removes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GO&lt;/code&gt; keywords and blank lines.&lt;/p&gt;

&lt;p&gt;And the last part turns the TSV files into INSERT statements. It grabs table names from the file name and removes the base path and the TSV extension. It assumes reference tables only have an id and a name.&lt;/p&gt;

&lt;p&gt;With this compact script file, I removed the old migration files except the last one. For the project in the screenshot above, I kept &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Migration0027&lt;/code&gt;. Then, I used all the SQL statements from the dump file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Up()&lt;/code&gt; step of the migration. I had an squash migration after that.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how I squashed old migrations in one of my client’s projects using schemazen and a Bash script. The idea is to squash our migrations after every stable release of our projects. From the reference article, one commenter said he does this approach one or twice a year. Another one, after every breaking changes.&lt;/p&gt;

&lt;p&gt;By the way, recently, I got interested in the Unix tools again. Check &lt;a href=&quot;/2022/12/10/ReplaceKeywordInFile/&quot;&gt;how to replace keywords in a file name and content with Bash&lt;/a&gt; and &lt;a href=&quot;/2022/12/15/CreateProjectStructureWithDotNetCli/&quot;&gt;how to create ASP.NET Core Api project structure with dotnet cli&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Lessons I learned as a code reviewer</title>
   <link href="https://canro91.github.io/2022/12/19/LessonsAsReviewer/"/>
   <updated>2022-12-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/19/LessonsAsReviewer</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I’ve noticed that most of the comments fall into two categories. I will call them “babysitting” and “surprising solution.”&lt;/p&gt;

&lt;h2 id=&quot;1-babysitting&quot;&gt;1. Babysitting&lt;/h2&gt;

&lt;p&gt;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.”&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;Visual Studio extension&lt;/a&gt;. And let’s turn all warnings into compilation errors.&lt;/p&gt;

&lt;p&gt;For example, with this idea of automation in mind, I ended up writing a &lt;a href=&quot;/2023/09/18/FormatSqlFilesOnCommit/&quot;&gt;Git pre-commit hook to format sql files&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-surprising-solution&quot;&gt;2. Surprising solution&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Also as a reviewer, I learned to stop using &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;leading or tricky questions&lt;/a&gt;. And I taught to &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;use simple test values to write good unit tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re new to code reviews, check these &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;Tips for better code reviews&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Lessons I learned from my ex-coworkers about software engineering</title>
   <link href="https://canro91.github.io/2022/12/18/LessonsFromExCoworkers/"/>
   <updated>2022-12-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/18/LessonsFromExCoworkers</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For better or worse, we all have something to learn from our bosses and co-workers.&lt;/p&gt;

&lt;p&gt;These are three lessons I learned from three of my ex-coworkers and ex-bosses about software engineering, designing, and programming.&lt;/p&gt;

&lt;p&gt;I didn’t take the time to thank them when I worked with them. This is my thank you note.&lt;/p&gt;

&lt;h2 id=&quot;1-inspire-change&quot;&gt;1. Inspire Change&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.”&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In emergencies, while everybody panicked, Edgardo was calm, going through log files and running diagnostics.&lt;/p&gt;

&lt;h2 id=&quot;2-stand-on-the-shoulders-of-giants&quot;&gt;2. Stand on the shoulders of giants&lt;/h2&gt;

&lt;p&gt;From Javier, the architect, I learned to stand on the shoulder of giants.&lt;/p&gt;

&lt;p&gt;When we ran into issues, he always said &lt;em&gt;“you’re not the first one solving that problem”&lt;/em&gt; and &lt;em&gt;“smarter people have already solved that.”&lt;/em&gt; He made us look out there first.&lt;/p&gt;

&lt;p&gt;Every time I’m tempted to start something from scratch, I start looking at GitHub. Maybe I can stand on somebody else’s shoulders.&lt;/p&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2 id=&quot;3-identify-your-users-and-their-goals&quot;&gt;3. Identify your users and their goals&lt;/h2&gt;

&lt;p&gt;From Pedro, the boss, I learned to keep in mind who our end users are.&lt;/p&gt;

&lt;p&gt;More than once, I remember Pedro asking designers to change fonts and increase their size. He said: &lt;em&gt;“you aren’t the one who’s going to use this app. This is for your dad and granddad. This is for oldies.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;“90% of the time, those documents are valid.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;For more career lessons, check &lt;a href=&quot;/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer/&quot;&gt;things I wished I knew before becoming a software engineer&lt;/a&gt;, &lt;a href=&quot;/2020/08/08/LessonsOnRemoteWork/&quot;&gt;ten lessons learned after one year of remote work&lt;/a&gt;, and &lt;a href=&quot;/2022/12/17/LessonsOnAFailedProject/&quot;&gt;things I learned after a failed project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Three Postmortem Lessons From a &quot;Failed&quot; Project</title>
   <link href="https://canro91.github.io/2022/12/17/LessonsOnAFailedProject/"/>
   <updated>2022-12-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/17/LessonsOnAFailedProject</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Software projects don’t fail because of the tech stack, programming languages, or frameworks.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;This is a failed software project that was killed by unclear expectations and poor communication.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Even though our team delivered the project, we made some mistakes and I learned a lesson or two.&lt;/p&gt;

&lt;h2 id=&quot;1-minimize-moving-parts&quot;&gt;1. Minimize Moving Parts&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;But, the new guideline was to “&lt;a href=&quot;/2022/10/03/HandsOnDDDTakeaways/&quot;&gt;start doing DDD&lt;/a&gt;.” That’s not a bad thing per se. The thing was: almost nobody in the team was familiar with DDD.&lt;/p&gt;

&lt;p&gt;I had worked with some of the DDD artifacts before. But, we didn’t know what the upper management wanted with “start doing DDD.”&lt;/p&gt;

&lt;p&gt;With this decision, a one-month project ended up being behind schedule.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;For such a small project with a tight schedule, there was no room for experimentation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“The best tool for a job is the tool you already know.”&lt;/em&gt; At that time, the best tool for my team was N-tier architecture.&lt;/p&gt;

&lt;p&gt;For my future projects, I will minimize the moving parts.&lt;/p&gt;

&lt;h2 id=&quot;2-define-a-clear-path&quot;&gt;2. Define a Clear Path&lt;/h2&gt;

&lt;p&gt;We agreed on reading the “guests” and “reservations” tables inside a &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;background processor&lt;/a&gt; to call the third-party APIs. And we stared working on it.&lt;/p&gt;

&lt;p&gt;But another team member was analyzing how to implement an Event-Driven solution with a message queue.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;For my future projects, I will define a clear path and have everybody on-boarded.&lt;/p&gt;

&lt;h2 id=&quot;3-dont-get-distracted-cross-the-finish-line&quot;&gt;3. Don’t Get Distracted. Cross the Finish Line&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;We started to estimate with poker planning.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;It was time to focus on the goal and not get distracted by unproductive ceremonies or meetings. I don’t mean stop &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;writing unit tests&lt;/a&gt; or &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;doing code reviews&lt;/a&gt;. Those were the minimum safety procedures for the team.&lt;/p&gt;

&lt;p&gt;For my future projects, I will focus on crossing the finish line.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I bet we can find fail projects using the most state-of-the-art technology or programming languages.&lt;/p&gt;

&lt;p&gt;Like marriages, software projects fail because of unclear expectations and poor communication. This was one of them.&lt;/p&gt;

&lt;p&gt;For more career lessons, check my &lt;a href=&quot;/2019/08/19/FiveLessonsAfterFiveYears/&quot;&gt;five lessons after five years as a software developer&lt;/a&gt;, &lt;a href=&quot;/2020/08/08/LessonsOnRemoteWork/&quot;&gt;ten lessons learned after one year of remote work&lt;/a&gt; and &lt;a href=&quot;/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer/&quot;&gt;things I wished I knew before working as a software engineer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Six helpful extension methods I use to work with Collections</title>
   <link href="https://canro91.github.io/2022/12/16/HelperMethodsOnCollections/"/>
   <updated>2022-12-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/16/HelperMethodsOnCollections</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2 id=&quot;1-check-if-a-collection-is-null-or-empty&quot;&gt;1. Check if a collection is null or empty&lt;/h2&gt;

&lt;p&gt;These are three methods to check if a collection is null or empty. They’re wrappers around &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;the LINQ Any method&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNullWhen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsNotNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;([&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNullWhen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[NotNullWhen]&lt;/code&gt; attribute to let the compiler know if the source collection is null. This way, when we turn on the &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;nullable references feature&lt;/a&gt;, the compiler can generate more accurate warnings. If we don’t add this attribute, we get some false positives. Like this one,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// CS8604: Possible null reference argument for parameter &apos;source&apos;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// But we don&apos;t want this warning here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-emptyifnull&quot;&gt;2. EmptyIfNull&lt;/h2&gt;

&lt;p&gt;In the same spirit of &lt;a href=&quot;/2020/11/17/DefaultOrEmpty/&quot;&gt;DefaultIfEmpty&lt;/a&gt;, 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.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EmptyIfNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enumerable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example, we can write,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;someNullableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EmptyIfNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of writing,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;someNullableCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoSomeMapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I found this idea in &lt;a href=&quot;https://coding.abel.nu/2012/02/null-handling-with-extension-methods/&quot;&gt;Pasion for Coding’s Null Handling with Extension Methods&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-enumerated&quot;&gt;3. Enumerated&lt;/h2&gt;

&lt;p&gt;The LINQ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select()&lt;/code&gt; method has an overload to map elements using their position in the source collection. We can use it like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Inspired by &lt;a href=&quot;https://developer.apple.com/documentation/swift/array/enumerated()&quot;&gt;Swift Enumerated method&lt;/a&gt;, we can write a wrapper around this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select()&lt;/code&gt; overload. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enumerated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example, we can write,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enumerated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/2024/04/15/NET9LinqMethods/&quot;&gt;.NET 9 introduced the Index method&lt;/a&gt; that works like our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerated()&lt;/code&gt;. We don’t need to roll our own method anymore in recent versions of .NET.&lt;/p&gt;

&lt;p&gt;Voilà! These are some of my favorite extension methods to work with collections. Some of them are workarounds to &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;avoid the NullReferenceException&lt;/a&gt; when working with collections. What extension methods do you use often?&lt;/p&gt;

&lt;p&gt;If you want to learn more about LINQ, read my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;Quick Guide to LINQ&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create ASP.NET Core Api project structure with dotnet cli</title>
   <link href="https://canro91.github.io/2022/12/15/CreateProjectStructureWithDotNetCli/"/>
   <updated>2022-12-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/15/CreateProjectStructureWithDotNetCli</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While looking at &lt;a href=&quot;https://csadvent.christmas/&quot;&gt;C# Advent 2022&lt;/a&gt;, I found the Humble Toolsmith page and its post &lt;a href=&quot;https://humbletoolsmith.com/2022/08/18/quickly-create-test-solutions-by-scripting-the-dotnet-cli/&quot;&gt;Create Test Solutions by Scripting the Dotnet CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;h2 id=&quot;how-to-create-project-structure-with-dotnet-cli&quot;&gt;How to create project structure with dotnet cli&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Change to suit your own needs&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;Prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Acme.CoolProject
&lt;span class=&quot;c&quot;&gt;#      ^^^^^&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Change it to use your project name prefix&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 1. Create solution&lt;/span&gt;
dotnet new sln &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Api

&lt;span class=&quot;c&quot;&gt;# 2. Create src projects&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Create class libraries&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;name &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Data&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Domain&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Infrastructure&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Messages&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Optionally:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# dotnet new classlib -o $Prefix.$name/src -n $name&lt;/span&gt;
dotnet new classlib &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;
dotnet sln add src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;.csproj &lt;span class=&quot;nt&quot;&gt;--in-root&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Create Console projects&lt;/span&gt;
dotnet new console &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data.Migrator
dotnet sln add src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data.Migrator/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data.Migrator.csproj &lt;span class=&quot;nt&quot;&gt;--in-root&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Create Api projects&lt;/span&gt;
dotnet new webapi &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Api
dotnet sln add src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Api/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Api.csproj &lt;span class=&quot;nt&quot;&gt;--in-root&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Api depends on Data, Infrastructure, and Messages&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;dependsOn &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Data&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Infrastructure&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Messages&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
&lt;/span&gt;dotnet add src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Api/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Api.csproj reference src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$dependsOn&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$dependsOn&lt;/span&gt;.csproj
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Data depends on Domain and Infrastructure&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;dependsOn &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Domain&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Infrastructure&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
&lt;/span&gt;dotnet add src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data.csproj reference src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$dependsOn&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$dependsOn&lt;/span&gt;.csproj
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Data.Migrator depends on Data&lt;/span&gt;
dotnet add src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data.Migrator/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data.Migrator.csproj reference src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Data.csproj

&lt;span class=&quot;c&quot;&gt;# Infrastructure depends on Domain and Messages&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;dependsOn &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Domain&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Messages&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
&lt;/span&gt;dotnet add src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Infrastructure/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.Infrastructure.csproj reference src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$dependsOn&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$dependsOn&lt;/span&gt;.csproj
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 3. Create test projects&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;name &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Api&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Data&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Domain&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Infrastructure&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
&lt;/span&gt;dotnet new mstest &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tests/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;.Tests
dotnet sln add tests/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;.Tests/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;.Tests.csproj &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; Tests
dotnet add tests/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;.Tests/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;.Tests.csproj reference src/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$Prefix&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;.csproj
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 4. Copy template files&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# .gitignore, .editorconfig, .dockerignore&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Copy dotfiles&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;file &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.cs&quot;&lt;/span&gt; ~/Documents/_Projects/_FolderStructure/Templates/&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; ~/Documents/_Projects/_FolderStructure/Templates/&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 5. Cleanup&lt;/span&gt;
find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;WeatherForecastController.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;nt&quot;&gt;-delete&lt;/span&gt;
find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;WeatherForecast.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;nt&quot;&gt;-delete&lt;/span&gt;

find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Class1.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;nt&quot;&gt;-delete&lt;/span&gt;
find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UnitTest1.cs&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;nt&quot;&gt;-delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When I need to create a new project, I only change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Prefix&lt;/code&gt; at the top of the file.&lt;/p&gt;

&lt;p&gt;Notice this script copies some template files (.gitignore, .editorconfig, .dockerignore) from a shared location.&lt;/p&gt;

&lt;p&gt;This script creates a project structure like this:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-12-15-CreateProjectStructureWithDotNetCli/ProjectListInVS.png&quot; alt=&quot;Project list in Visual Studio&quot; /&gt;
    &lt;figcaption&gt;ASP.NET Core Api project structure inside Visual Studio&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And a folder structure like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;│   Acme.CoolProject.Api.sln
├───src
│   ├───Acme.CoolProject.Api
│   ├───Acme.CoolProject.Data
│   ├───Acme.CoolProject.Data.Migrator
│   ├───Acme.CoolProject.Domain
│   ├───Acme.CoolProject.Infrastructure
│   └───Acme.CoolProject.Messages
└───tests
    ├───Acme.CoolProject.Api.Tests
    ├───Acme.CoolProject.Data.Tests
    ├───Acme.CoolProject.Domain.Tests
    └───Acme.CoolProject.Infrastructure.Tests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the Messages project, I put input and output view models. And, when doing CQRS, I put commands and queries. In the Migrator project, I put the &lt;a href=&quot;/2020/08/15/Simple.Migrations/&quot;&gt;Simple.Migrations runner and migrations to update the database schema&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With small tweaks, we can change the folder structure to have the component folders on top and the /src and /test folders inside them. Like,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;│   Acme.CoolProject.Api.sln
├───Api
    ├───src
    └───tests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even we can create folders and csproj files with shorter names by passing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt; flag and a name in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet new&lt;/code&gt; command.&lt;/p&gt;

&lt;h2 id=&quot;how-to-update-the-csproj-files-with-powershell&quot;&gt;How to update the csproj files with Powershell&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$projects&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-ChildItem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;csproj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Recurse&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Exclude&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Tests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;csproj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$projects&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$proj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        
        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$propertyGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$proj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PropertyGroup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TargetFramework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shouldSave&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$propertyGroup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RootNamespace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-eq&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RootNamespace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$propertyGroup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ParentNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ParentNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;RootNamespace&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$propertyGroup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$RootNamespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out-null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$propertyGroup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RootNamespace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Acme.CoolProject&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shouldSave&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        
        &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$shouldSave&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$proj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;RootNamespace added to &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$path&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System.Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;To read more content, check &lt;a href=&quot;/2022/12/09/RenameProjectsVisualStudio/&quot;&gt;How to quickly rename projects inside a Visual Studio solution&lt;/a&gt; and &lt;a href=&quot;/2022/12/10/ReplaceKeywordInFile/&quot;&gt;How to rename a keyword in file contents and names&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to write good unit tests: Use simple test values</title>
   <link href="https://canro91.github.io/2022/12/14/SimpleTestValues/"/>
   <updated>2022-12-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/14/SimpleTestValues</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I had to review some code that had one method to merge dictionaries. This is one of the suggestions I gave during that review to write good unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write good unit tests, write the Arrange part of tests using the simplest test values that exercise the scenario under test. Avoid building large object graphs and using magic numbers in the Arrange part of tests.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;here-are-the-tests-i-reviewed&quot;&gt;Here are the tests I reviewed&lt;/h2&gt;

&lt;p&gt;These are two of the unit tests I reviewed. They test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Merge()&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Collections.Generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Linq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyProject.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DictionaryExtensionsTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Merge_NoDuplicates_DoesNotMergeNullAndEmptyOnes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;70&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;90&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;three&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;110&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;four&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;120&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;130&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;140&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;150&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;160&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;170&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;180&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;190&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;four&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyRange&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Merge_DuplicateKeys_ReturnNoDuplicates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;70&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;90&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;three&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;three&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyRange&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yes, those are the real tests I had to review. I slightly changed the namespaces and the test names.&lt;/p&gt;

&lt;h2 id=&quot;whats-wrong&quot;&gt;What’s wrong?&lt;/h2&gt;

&lt;p&gt;Let’s take a closer look at the first test. Do we need six dictionaries to test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Merge()&lt;/code&gt; method? No! And do we need 19 items? No! We can still cover the same scenario with only two single-item dictionaries without duplicate keys.&lt;/p&gt;

&lt;p&gt;And let’s write separate tests to deal with edge cases. Let’s write one test to work with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and another one with an empty dictionary. Again two dictionaries will be enough for each test.&lt;/p&gt;

&lt;p&gt;Having too many dictionaries with too many items made us write that funny &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; with a funny multiplication inside. That’s why some of the values are multiplied by 10, and others aren’t. We don’t need that with a simpler scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unit tests should only have assignments without branching or looping logic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Looking at the second test, we noticed it followed the same pattern as the first one. Too many items and a weird &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; with a multiplication inside.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1533090161767-e6ffed986c88?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY1Njk1NDExMg&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;A simple bedroom&quot; /&gt;

&lt;figcaption&gt;Let&apos;s embrace simplicity. Photo by &lt;a href=&quot;https://unsplash.com/@srosinger3997?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Samantha Gades&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/simplicity?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;write-tests-using-simple-test-values&quot;&gt;Write tests using simple test values&lt;/h2&gt;

&lt;p&gt;Let’s write our tests using simple test values to prepare our scenario under test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Merge_NoDuplicates_DoesNotMergeNullAndEmptyOnes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// One test to Merge a dictionary with an empty one&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Another test to Merge a dictionary with a null one&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Merge_DuplicateKeys_ReturnNoDuplicates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duplicateKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duplicateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;two&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duplicateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duplicateKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;merged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice this time, we boiled down &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;the Arrange part&lt;/a&gt; of the first test to only two dictionaries with one item each, without duplicates.&lt;/p&gt;

&lt;p&gt;And for the second one, the one for duplicates, we wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;duplicateKey&lt;/code&gt; variable and used it in both dictionaries as key to &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;make the test scenario obvious&lt;/a&gt;. This way, after reading the test name, we don’t have to decode where the duplicate keys are.&lt;/p&gt;

&lt;p&gt;Since we wrote simple tests, we could remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; in the Assert parts and the funny multiplications.&lt;/p&gt;

&lt;p&gt;The test for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and empty cases are exercises left to the reader. They’re not difficult to write.&lt;/p&gt;

&lt;p&gt;Voilà! That’s another tip to write good unit tests. Let’s strive to have tests easier to follow with simple test values. Here we used dictionaries, but we can follow this tip when writing integration tests for the database. Often to prepare our test data, we insert multiple records when only one or two are enough to prove our point.&lt;/p&gt;

&lt;p&gt;Also, I wrote other posts about how to write good unit tests. One to &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;reduce noisy tests and use explicit test values&lt;/a&gt; and another one to &lt;a href=&quot;/2021/02/05/FailingTest/&quot;&gt;write a failing test first&lt;/a&gt;. Don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Five or more lessons I learned after working with Hangfire and OrmLite</title>
   <link href="https://canro91.github.io/2022/12/13/LessonsOnHangfireAndOrmLite/"/>
   <updated>2022-12-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/13/LessonsOnHangfireAndOrmLite</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I finished another internal project while working with one of my clients. I worked to connect a Property Management System with a third-party Point of Sales. I had to work with Hangfire and OrmLite. I used &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;Hangfire to replace ASP.NET BackgroundServices&lt;/a&gt;. Today I want to share some of the technical things I learned along the way.&lt;/p&gt;

&lt;h2 id=&quot;1-hangfire-lazy-loads-configurations&quot;&gt;1. Hangfire lazy-loads configurations&lt;/h2&gt;

&lt;p&gt;Hangfire lazy loads configurations. We have to retrieve services from the ASP.NET Core dependencies container instead of using static alternatives.&lt;/p&gt;

&lt;p&gt;I faced this issue after trying to run Hangfire in non-development environments without registering the Hangfire dashboard. This was the exception message I got: &lt;em&gt;“JobStorage.Current property value has not been initialized.”&lt;/em&gt; When registering the Dashboard, Hangfire loads some of those configurations. That’s why “it worked on my machine.”&lt;/p&gt;

&lt;p&gt;These two issues in Hangfire GitHub repo helped me to find this out: &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/issues/1991&quot;&gt;issue #1991&lt;/a&gt; and &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/issues/1967&quot;&gt;issue #1967&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This was the fix I found in those two issues:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyCoolProjectWithHangfire.Jobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyCoolProjectWithHangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WebApplicationExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureRecurringJobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Before, using the static version:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// RecurringJob.AddOrUpdate&amp;lt;MyCoolJob&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    MyCoolJob.JobId,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    x =&amp;gt; x.DoSomethingAsync());&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// RecurringJob.Trigger(MyCoolJob.JobId);&lt;/span&gt;
				
        &lt;span class=&quot;c1&quot;&gt;// After:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recurringJobManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetRequiredService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IRecurringJobManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;recurringJobManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCoolJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;MyCoolJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
			
        &lt;span class=&quot;n&quot;&gt;recurringJobManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCoolJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-hangfire-dashboard-in-non-local-environments&quot;&gt;2. Hangfire Dashboard in non-Local environments&lt;/h2&gt;

&lt;p&gt;By default, Hangfire only shows the Dashboard for local requests. A coworker pointed that out. It’s in plain sight in &lt;a href=&quot;https://docs.hangfire.io/en/latest/configuration/using-dashboard.html&quot;&gt;the Hangfire Dashboard documentation&lt;/a&gt;. Arrrggg!&lt;/p&gt;

&lt;p&gt;To make it work in other non-local environments, we need an authorization filter. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AllowAnyoneAuthorizationFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDashboardAuthorizationFilter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Authorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DashboardContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Everyone is more than welcome...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we add it when registering the Dashboard into the dependencies container. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseHangfireDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/hangfire&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DashboardOptions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Authorization&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AllowAnyoneAuthorizationFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-inmemory-hangfire-succeededjobs-method&quot;&gt;3. InMemory-Hangfire SucceededJobs method&lt;/h2&gt;

&lt;p&gt;For the In-Memory Hangfire implementation, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SucceededJobs()&lt;/code&gt; method from the monitoring API returns jobs from most recent to oldest. There’s no need for pagination. Look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reverse()&lt;/code&gt; method in the &lt;a href=&quot;https://github.com/HangfireIO/Hangfire.InMemory/blob/master/src/Hangfire.InMemory/InMemoryMonitoringApi.cs#L308&quot;&gt;SucceededJobs() source code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I had to find out why an ASP.NET health check was only working the first time. It turned out that the code was paginating the successful jobs, always looking for the oldest successful jobs. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HangfireSucceededJobsHealthCheck&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHealthCheck&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CheckLastJobsCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_period&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HangfireSucceededJobsHealthCheck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;period&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_period&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;period&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HealthCheckResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckHealthAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HealthCheckContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isHealthy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitoringApi&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JobStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMonitoringApi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// It used pagination to bring the oldest 10 jobs&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// var succeededCount = (int)monitoringApi.SucceededListCount();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// var succeededJobs = monitoringApi.SucceededJobs(succeededCount - CheckLastJobsCount, CheckLastJobsCount);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                                 ^^^^^&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// After:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// SucceededJobs returns jobs from newest to oldest &lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;succeededJobs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitoringApi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SucceededJobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CheckLastJobsCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                            ^^^^^  &lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;successJobsCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;succeededJobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SucceededAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HasValue&lt;/span&gt;
                                  &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SucceededAt&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;period&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;successJobsCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HealthCheckResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Healthy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Yay! We have succeeded jobs.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HealthCheckResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Registration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FailureStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Nein! We don&apos;t have succeeded jobs.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is so confusing that there’s an &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/issues/2160&quot;&gt;issue on the Hangfire repo&lt;/a&gt; asking for clarification. Not all storage  implementations return successful jobs in reverse order. Arrrggg!&lt;/p&gt;

&lt;h2 id=&quot;4-prevent-concurrent-execution-of-hangfire-jobs&quot;&gt;4. Prevent Concurrent execution of Hangfire jobs&lt;/h2&gt;

&lt;p&gt;Hangfire has an attribute to prevent the concurrent execution of the same job: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DisableConcurrentExecutionAttribute&lt;/code&gt;. &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/blob/master/src/Hangfire.Core/DisableConcurrentExecutionAttribute.cs&quot;&gt;Source&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DisableConcurrentExecution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeoutInSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyCoolJob&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even we can change the resource being locked to avoid executing jobs with the same parameters. For example, we can run only one job per client simultaneously, like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyCoolJob&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DisableConcurrentExecution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;clientId:{0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                          ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-ormlite-ignoreonupdate-sqlscalar-and-createindex&quot;&gt;5. OrmLite IgnoreOnUpdate, SqlScalar, and CreateIndex&lt;/h2&gt;

&lt;p&gt;OrmLite has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnUpdate]&lt;/code&gt; attribute. I found this attribute when reading OrmLite source code. When using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt;, OrmLite omits properties marked with this attribute when generating the SQL statement. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs#L810&quot;&gt;Source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QueryFirst()&lt;/code&gt; method requires an explicit transaction as a parameter. Unlike &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlScalar()&lt;/code&gt; which uses the same transaction from the input database connection. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/src/ServiceStack.OrmLite/OrmLiteReadApi.cs#L524&quot;&gt;Source&lt;/a&gt;. I learned this because I had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoesIndexExists()&lt;/code&gt; method inside a database migration and it failed with the message &lt;em&gt;“ExecuteReader requires the command to have a transaction…“&lt;/em&gt; This is what I had to change,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoesIndexExist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDbConnection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doesIndexExistSql&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;
&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;;
&lt;/span&gt;    
    &lt;span class=&quot;c1&quot;&gt;// Before:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// return connection.QueryFirst&amp;lt;bool&amp;gt;(isIndexExistsSql);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Exception: ExecuteReader requires the command to have a transaction...&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// After:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SqlScalar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doesIndexExistSql&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                      ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, by looking at OrmLite source code, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateIndex()&lt;/code&gt; method, by default, creates indexes with names like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idx_TableName_FieldName&lt;/code&gt;. Then we can omit the index name parameter when working with this method. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.OrmLite/blob/master/src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs#L1494&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! That’s what I learned from this project. This gave me the idea to stop to reflect on what I learned from every project I work on. I really enjoyed figuring out the issue with the health check. It made me read the source code of the In-memory storage for Hangfire.&lt;/p&gt;

&lt;p&gt;For more content, check how I use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IgnoreOnUpdate&lt;/code&gt; attribute to &lt;a href=&quot;/2022/12/11/AuditFieldsWithOrmLite/&quot;&gt;automatically insert and update audit fields with OrmLite&lt;/a&gt;, &lt;a href=&quot;/2023/06/26/PassDataTableOrmLite/&quot;&gt;how to pass a DataTable as a parameter with OrmLite&lt;/a&gt; and &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;how to replace BackgroundServices with a lite Hangfire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Four Lessons I Wished I Knew Before Becoming a Software Engineer</title>
   <link href="https://canro91.github.io/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer/"/>
   <updated>2022-12-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It has been more than 10 years since I started working as a Software Engineer.&lt;/p&gt;

&lt;p&gt;I began designing reports by hand using iTextSharp. And by hand, I mean drawing lines and pixels on a blank canvas. Arrggg!&lt;/p&gt;

&lt;p&gt;I used Visual Studio 2010 and &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;learned about LINQ&lt;/a&gt; for the first time those days.&lt;/p&gt;

&lt;p&gt;Then I moved to some sort of full-stack role writing DotNetNuke modules with Bootstrap and Knockout.js.&lt;/p&gt;

&lt;p&gt;In more recent years, I switched to work as a backend engineer. I got tired of getting feedback on colors, alignment, and other styling issues. They’re important. But that’s not the work I enjoy doing.&lt;/p&gt;

&lt;p&gt;If I could start all over again, these are four lessons I wished I knew before becoming a Software Engineer again.&lt;/p&gt;

&lt;h2 id=&quot;1-find-a-way-to-stand-out-make-yourself-different&quot;&gt;1. Find a Way To Stand Out: Make Yourself Different&lt;/h2&gt;

&lt;p&gt;Learning a second language is a perfect way to stand out. I’m a bit biased since &lt;a href=&quot;/2020/10/23/ThreeLanguageLessons/&quot;&gt;language learning&lt;/a&gt; is one of my hobbies.&lt;/p&gt;

&lt;p&gt;For most of us, standing out means learning English as a second language.&lt;/p&gt;

&lt;p&gt;A second language opens doors to new markets, professional relationships, and job opportunities. And, you can brag about a second and third language on your CV.&lt;/p&gt;

&lt;p&gt;After an interview, you can be remembered for the languages you speak. “Ah! The guy who speaks languages.”&lt;/p&gt;

&lt;h2 id=&quot;2-never-stop-learning&quot;&gt;2. Never Stop Learning&lt;/h2&gt;

&lt;p&gt;Let’s be honest. University will teach you lots of subjects. Probably, you don’t need most of them and the ones you need you will have to study them on your own.&lt;/p&gt;

&lt;p&gt;You will have to study books, watch online conferences, and read blog posts. Never stop learning! That would keep you in the game in the long run.&lt;/p&gt;

&lt;p&gt;But, it can be daunting if you try to learn everything about everything. “Learn something about everything, and everything about something,” says popular wisdom.&lt;/p&gt;

&lt;p&gt;Libraries and frameworks come and go. Stick to the principles.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1616400619175-5beda3a17896?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0NjU4NjU2Ng&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Desktop, laptop and notebook&quot; /&gt;

&lt;figcaption&gt;Always keep learning. Photo by &lt;a href=&quot;https://unsplash.com/@imkirk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Iewek Gnos&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/learning?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-have-an-escape-plan&quot;&gt;3. Have an Escape Plan&lt;/h2&gt;

&lt;p&gt;There is no safe place to work. Period! Full stop!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2023/08/21/OnLayoffs/&quot;&gt;Companies lay off employees&lt;/a&gt; without any further notice and apparent reason. You can get seriously injured or sicked. You won’t be able to work forever.&lt;/p&gt;

&lt;p&gt;If you’re reading this from the future, ask your parents or grandparents about the year 2020. Lots of people lost their jobs or got their salaries cut by half in a few days. And there were nothing they could do about it.&lt;/p&gt;

&lt;p&gt;Have an escape plan. A side income, your own business, a hobby you can turn into a profitable idea. You name it!&lt;/p&gt;

&lt;p&gt;Apart from an escape plan, have an emergency fund. The book “The Simple Path to Wealth” calls emergency funds: “F-you” money. Keep enough savings in your account to avoid worrying about when to leave a job or when the choice isn’t yours.&lt;/p&gt;

&lt;h2 id=&quot;4-have-an-active-online-presence&quot;&gt;4. Have an Active Online Presence&lt;/h2&gt;

&lt;p&gt;If I could do something different, I would have an active online presence way earlier.&lt;/p&gt;

&lt;p&gt;Be active online. Have a blog, a LinkedIn profile, or a professional profile on any other social network. Use social networks to your advantage.&lt;/p&gt;

&lt;p&gt;In the beginning, you might think you don’t know enough to start writing or &lt;a href=&quot;/2020/07/18/HowIStartedBlogging/&quot;&gt;start a blog&lt;/a&gt;. But you can share what you learn, the resources you use to learn, and your sources of inspiration. You can learn in public and &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;show your work&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! These are four lessons I wished I knew before starting a software engineer career. Remember, every journey is different and we’re all figuring out life. For sure, my circumstances have been different than yours, and that’s why these four lessons.&lt;/p&gt;

&lt;p&gt;In any case, “Your career is your responsibility, not your employer’s.” I learned that from &lt;a href=&quot;/2020/06/15/CleanCoder/&quot;&gt;The Clean Coder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Interested in more career lessons? Check &lt;a href=&quot;/2019/08/19/FiveLessonsAfterFiveYears/&quot;&gt;five lessons I learned on my first five years as software engineer&lt;/a&gt;,  &lt;a href=&quot;/2020/08/08/LessonsOnRemoteWork/&quot;&gt;ten lessons learned after one year of remote work&lt;/a&gt;, and &lt;a href=&quot;/2023/09/04/AgainstMassiveUnrequestedRefactorings/&quot;&gt;a case against massive unrequested refactorings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to automatically insert and update audit fields with OrmLite</title>
   <link href="https://canro91.github.io/2022/12/11/AuditFieldsWithOrmLite/"/>
   <updated>2022-12-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/11/AuditFieldsWithOrmLite</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I had to work with &lt;a href=&quot;https://docs.servicestack.net/ormlite/&quot;&gt;OrmLite&lt;/a&gt;. I had to follow the convention of adding audit fields in all of the database tables. Instead of adding them manually, I wanted to populate them when using OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt; method. This is how to automatically insert and update audit fields with OrmLite.&lt;/p&gt;

&lt;h2 id=&quot;1-create-a-1-to-1-mapping-between-two-tables&quot;&gt;1. Create a 1-to-1 mapping between two tables&lt;/h2&gt;

&lt;p&gt;Let’s store our favorite movies. Let’s create two classes, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt;, to represent a one-to-one relationship between movies and their directors.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IAudit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAudit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAudit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;References&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// OrmLite expects a foreign key back to the Movie table&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we used OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Reference]&lt;/code&gt; to tie every director to his movie. With these two classes, OrmLite expects two tables and a foreign key from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt; pointing back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt;. Also, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IAudit&lt;/code&gt; to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatedDate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateDate&lt;/code&gt; properties. We will use this interface in the next step.&lt;/p&gt;

&lt;h2 id=&quot;2-use-ormlite-insert-and-update-filters&quot;&gt;2. Use OrmLite Insert and Update Filters&lt;/h2&gt;

&lt;p&gt;To automatically set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatedDate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdatedDate&lt;/code&gt; when inserting and updating movies, let’s use OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InsertFilter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateFilter&lt;/code&gt;. With them, we can manipulate our records before putting them in the database.&lt;/p&gt;

&lt;p&gt;Let’s create a unit test to show how to use those two filters,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceStack.DataAnnotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceStack.OrmLite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OrmLiteAuditFields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PopulateAuditFieldsTest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SaveAsync_InsertNewMovie_PopulatesAuditFields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OrmLiteConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DialectProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlServerDialect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OrmLiteConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAudit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auditRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;auditRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;auditRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OrmLiteConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAudit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auditRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;auditRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;...Any SQL Server connection string here...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrmLiteConnectionFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlServerDialect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieToInsert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We&apos;re not setting CreatedDate and UpdatedDate here...&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;James Cameron&quot;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// We&apos;re not setting CreatedDate and UpdatedDate here, either...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movieToInsert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We insert &quot;Titanic&quot; for the first time&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// With &quot;references: true&quot;, we also insert the director&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insertedMovie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SingleByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insertedMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreNotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insertedMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreNotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insertedMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we defined the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InsertFilter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateFilter&lt;/code&gt; and inside them, we checked if the row to be inserted or updated implemented the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IAudit&lt;/code&gt; interface, to then set the audit fields with the current timestamp.&lt;/p&gt;

&lt;p&gt;To insert a movie and its director, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt; with the optional parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;references&lt;/code&gt; set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;. We didn’t explicitly set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatedDate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdatedDate&lt;/code&gt; properties before inserting a movie.&lt;/p&gt;

&lt;p&gt;Internally, OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt; either inserts or updates an object if it exists in the database. It uses the property annotated as the primary key to find if the object already exists in the database.&lt;/p&gt;

&lt;p&gt;Instead of using filters, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Default(OrmLiteVariables.SystemUtc)]&lt;/code&gt; to annotate our audit fields. With this attribute, OrmLite will create a default constraint. But, this will work only for the first insertion. Not for future updates on the same record.&lt;/p&gt;

&lt;h2 id=&quot;3-add-ignoreonupdate-for-future-updates&quot;&gt;3. Add [IgnoreOnUpdate] for future updates&lt;/h2&gt;

&lt;p&gt;To support future updates using the OrmLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt;, we need to annotate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatedDate&lt;/code&gt; property with the attribute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnUpdate]&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Director&lt;/code&gt; classes. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAudit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreOnUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAudit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AutoIncrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;References&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IgnoreOnUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Internally, when generating the SQL query for an UPDATE statement, OrmLite doesn’t include properties annotated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnUpdate]&lt;/code&gt;. &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.OrmLite/blob/f0a8241e6a88d56b73b11ea9c16656e8015256ea/src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs#L822&quot;&gt;Source&lt;/a&gt; Also, OrmLite has similar attributes for insertions and queries: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnInsertAttribute]&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnSelectAttribute]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let’s modify our previous unit test to insert and update a movie,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceStack.DataAnnotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ServiceStack.OrmLite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OrmLiteAuditFields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PopulateAuditFieldsTest&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SaveAsync_InsertNewMovie_PopulatesAuditFields&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;// Same OrmLiteConfig as before...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;...Any SQL Server connection string here...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrmLiteConnectionFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SqlServerDialect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieToInsert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We&apos;re not setting CreatedDate and UpdatedDate here...&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;James Cameron&quot;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// We&apos;re not setting CreatedDate and UpdatedDate here, either...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movieToInsert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 1.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We insert &quot;Titanic&quot; for the first time&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// With &quot;references: true&quot;, we also insert the director&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Let&apos;s give it some time...&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieToUpdate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//   ^^^^^&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// We&apos;re not setting CreatedDate and UpdatedDate here...&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//   ^^^^^&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;J. Cameron&quot;&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// We&apos;re not setting CreatedDate and UpdatedDate here, either...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movieToUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;references&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//       ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 2.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// To emulate a repository method, for example,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We&apos;re creating a new Movie object updating&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// movie and director names using the same Ids&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Often, when we work with repositories to abstract our data access layer, we update objects using the identifier of an already-inserted object and another object with the properties to update. Something like, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateAsync(movieId, aMovieWithSomePropertiesChanged)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Notice this time, after inserting a movie for the first time, we created a separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; instance (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movieToUpdate&lt;/code&gt;) keeping the same ids and updating the other properties. We used the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt; as before.&lt;/p&gt;

&lt;p&gt;At this point, if we don’t annotate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatedDate&lt;/code&gt; properties with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[IgnoreOnUpdate]&lt;/code&gt;, we get the exception: &lt;em&gt;“System.Data.SqlTypes.SqlTypeException: SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We don’t want to change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatedDate&lt;/code&gt; on updates. That’s why in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateFilter&lt;/code&gt; we only change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdatedDate&lt;/code&gt;. Since we’re using a different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; instance in the second &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SaveAsync()&lt;/code&gt; call, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatedDate&lt;/code&gt; stays uninitialized when OrmLite runs the UPDATE statement in the database. That’s why we got that exception.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to automate audit fields with OrmLite. After reading the OrmLite source code, I found out about these filters and attributes. I learned the lesson of reading our source code dependencies from a &lt;a href=&quot;/2022/01/17/MondayLinks/&quot;&gt;past Monday Links episode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To read more about OrmLite, check &lt;a href=&quot;/2023/06/26/PassDataTableOrmLite/&quot;&gt;how to pass a DataTable as a parameter with OrmLite&lt;/a&gt;, &lt;a href=&quot;/2023/10/16/SubqueriesAndOrmLite/&quot;&gt;how to join to subqueries with OrmLite&lt;/a&gt;, and &lt;a href=&quot;/2023/10/30/LessonsOnOrmLite/&quot;&gt;five lessons while working with OrmLite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to replace keywords in a file name and content with Bash</title>
   <link href="https://canro91.github.io/2022/12/10/ReplaceKeywordInFile/"/>
   <updated>2022-12-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/10/ReplaceKeywordInFile</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I needed to rename all occurrences of one keyword with another in source files and file names. In one of my client’s projects, I had to query one microservice to list a type of account to store it in an intermediate database. After a change in requirements, I had to query for another type of account and rename every place where I used the old one. This is what I learned.&lt;/p&gt;

&lt;h2 id=&quot;1-find-and-replace-inside-visual-studio&quot;&gt;1. Find and Replace inside Visual Studio&lt;/h2&gt;

&lt;p&gt;My original solution was to use Visual Studio to replace “OldAccount” with “NewAccount” in all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cs&lt;/code&gt; files in my solution. I used the “Replace in Files” menu by pressing: Ctrl + Shift + h,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-12-10-ReplaceKeywordInFile/ReplaceInFiles.png&quot; alt=&quot;Visual Studio &apos;Replace in Files&apos; menu&quot; width=&quot;400px&quot; /&gt;
    &lt;figcaption&gt;Visual Studio &apos;Replace in Files&apos; menu&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After this step, I replaced all occurrences inside source files. For example, it renamed class names from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOldAccountService&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INewAccountService&lt;/code&gt;. To rename variables, I repeated the same replace operation but using lowercase patterns.&lt;/p&gt;

&lt;p&gt;With the “Replace in Files” menu, I covered file content. But I still had to change the filenames. For example, I needed to rename &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOldAccountService.cs&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INewAccountService.cs&lt;/code&gt;. I did it by hand. Luckily, I didn’t have to replace many of them. There must be a better way!- I thought.&lt;/p&gt;

&lt;h2 id=&quot;2-find-and-replace-with-bash&quot;&gt;2. Find and Replace with Bash&lt;/h2&gt;

&lt;p&gt;After renaming my files by hand, I thought I could have used the command line to replace both the content and file names. I use Git Bash anyways. Therefore I have access to most of Unix commands.&lt;/p&gt;

&lt;h3 id=&quot;replace-old-with-new-inside-all-cs-files&quot;&gt;Replace ‘old’ with ‘new’ inside all .cs files&lt;/h3&gt;

&lt;p&gt;This is how to replace “Old” with “New” in all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cs&lt;/code&gt; files, &lt;a href=&quot;https://stackoverflow.com/a/12517022&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-irl&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--include&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\*&lt;/span&gt;.cs &lt;span class=&quot;s2&quot;&gt;&quot;Old&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-bi&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/Old/New/g&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; command, we look for all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cs&lt;/code&gt; files (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--include \*.cs&lt;/code&gt;) containing the “Old” keyword, no matter the case (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i&lt;/code&gt; flag), inside all child folders (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-r&lt;/code&gt;), showing only the file path (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-l&lt;/code&gt; flag).&lt;/p&gt;

&lt;p&gt;We could use the first command, before the pipe, to only list the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cs&lt;/code&gt; files containing a keyword.&lt;/p&gt;

&lt;p&gt;Then, with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; command, we replace the file content in place (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i&lt;/code&gt; flag), changing all occurrences of “Old” with “New” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s/Old/New/g&lt;/code&gt;). Notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g&lt;/code&gt; option in the replacement pattern. To avoid messing with line endings, we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-b&lt;/code&gt; flag. &lt;a href=&quot;https://stackoverflow.com/a/38998744&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we use spaces in filenames, that’s weird in source files but just in case, we need to tell &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; to use a different separator,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-irlZ&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--include&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\*&lt;/span&gt;.cs &lt;span class=&quot;s2&quot;&gt;&quot;Old&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-0&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-bi&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/Old/New/g&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Z&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-0&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xargs&lt;/code&gt;. &lt;a href=&quot;https://stackoverflow.com/a/17296705&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This first command and its variation does what Visual Studio “Find in Files” does.&lt;/p&gt;

&lt;h3 id=&quot;rename-old-with-new-in-filenames&quot;&gt;Rename ‘old’ with ‘new’ in filenames&lt;/h3&gt;

&lt;p&gt;Instead of renaming files by hand, this is how to replace “Old” with “New” in file names, &lt;a href=&quot;https://stackoverflow.com/a/71969517&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-path&lt;/span&gt; ./TestCoverageReport &lt;span class=&quot;nt&quot;&gt;-prune&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*Old*&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;p;s/Old/New/&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  | xargs &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;\n&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 2 &lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we’re using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt; command to “find” all files (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-type f&lt;/code&gt;), with “Old” anywhere in their names (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-name &quot;*Old*&quot;&lt;/code&gt;), inside the current folder (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt;), excluding the TestCoverageReport folder (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-path ./TestCoverageReport -prune&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Optionally, we can exclude multiple files by wrapping them inside parenthesis, like, &lt;a href=&quot;https://stackoverflow.com/a/4210072&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-path&lt;/span&gt; ./FolderToExclude &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-path&lt;/span&gt; ./AnotherFolderToExclude &lt;span class=&quot;se&quot;&gt;\)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-prune&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*Old*&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, we feed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; command to generate new names replacing “Old” with “New.” This time, we’re using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; option to print the “before” pattern. Up to this point, our command returns something like this,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./Some/Folder/AFileWithOld.cs
./Some/Folder/AFileWithNew.cs
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the last part, we split the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; output by the newline character and passed groups of two filenames to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mv&lt;/code&gt; command to finally rename the files.&lt;/p&gt;

&lt;p&gt;Another alternative to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mv&lt;/code&gt; would be to use the &lt;a href=&quot;http://plasmasturm.org/code/rename/&quot;&gt;rename command&lt;/a&gt;, like this,&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-path&lt;/span&gt; ./TestCoverageReport &lt;span class=&quot;nt&quot;&gt;-prune&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-type&lt;/span&gt; f &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*Old*&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  | xargs rename &lt;span class=&quot;s1&quot;&gt;&apos;s/Old/New/g&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how to replace a keyword in the content and name of files. It took me some time to figure this out. But, we can rename files with two one-liners. It will save us time in the future. Kudos to StackOverflow.&lt;/p&gt;

&lt;p&gt;To read more productivity tips and tools, check these &lt;a href=&quot;/2020/04/13/ProgramThatSave100Hours/&quot;&gt;programs that save me 100 hours&lt;/a&gt;, &lt;a href=&quot;/2020/09/02/TwoRecurringReviewComments/&quot;&gt;how to format commit messages with hooks&lt;/a&gt;, and &lt;a href=&quot;/2022/12/09/RenameProjectsVisualStudio/&quot;&gt;how to rename Visual Studio projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to rename Visual Studio projects and folders with Git</title>
   <link href="https://canro91.github.io/2022/12/09/RenameProjectsVisualStudio/"/>
   <updated>2022-12-09T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/09/RenameProjectsVisualStudio</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I had to rename all the projects inside a Visual Studio solution and the folders containing them. From &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SomeThing.Core&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Something.Core&lt;/code&gt;. That wasn’t the exact typo. But that’s the idea. Here it’s what I learned. It wasn’t as easy as only renaming the projects in Visual Studio.&lt;/p&gt;

&lt;h2 id=&quot;1-rename-folders-and-projects-manually&quot;&gt;1. Rename folders and projects manually&lt;/h2&gt;

&lt;p&gt;After digging into it, I found &lt;a href=&quot;https://stackoverflow.com/questions/2043618/proper-way-to-rename-solution-and-directories-in-visual-studio&quot;&gt;this StackOverflow answer&lt;/a&gt; to rename folders and projects inside a solution. It layouts a checklist of what to do. Here I’m bringing it with some extra steps:&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;close Visual Studio&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then, &lt;strong&gt;create a backup of the .sln file&lt;/strong&gt;. Just in case.&lt;/p&gt;

&lt;p&gt;Next, &lt;strong&gt;use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git mv&lt;/code&gt; to rename the folder from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SomeThing&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Something&lt;/code&gt;.&lt;/strong&gt; This preserves the history of the file. Also, rename the csproj files to match the new name.&lt;/p&gt;

&lt;p&gt;Use the next bash script to rename the folders and csproj files inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tests&lt;/code&gt; folders.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Replace these two values, please&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SomeThing
&lt;span class=&quot;nv&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Something

&lt;span class=&quot;c&quot;&gt;# Put here all others folders...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;folder &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Core&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Infrastructure&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do
&lt;/span&gt;git &lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;src/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt; src/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.Temp
git &lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;src/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.Temp src/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;
git &lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;src/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.csproj src/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.csproj

git &lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;tests/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.Tests tests/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.Tests.Temp
git &lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;tests/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.Tests.Temp tests/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.Tests
git &lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;src/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$old&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.csproj src/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$new&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$folder&lt;/span&gt;.csproj
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that it uses a temp folder to rename the containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SomeThing&lt;/code&gt; folder. I found that workaround in &lt;a href=&quot;https://stackoverflow.com/questions/3011625/git-mv-and-only-change-case-of-directory&quot;&gt;this StackOverflow answer&lt;/a&gt;. Git doesn’t go along with different casings in file names.&lt;/p&gt;

&lt;p&gt;Next, &lt;strong&gt;in the .sln file, edit all instances of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SomeThing&lt;/code&gt; to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Something&lt;/code&gt;&lt;/strong&gt;, using a text editor like NotePad++.&lt;/p&gt;

&lt;p&gt;Next, &lt;strong&gt;restart Visual Studio&lt;/strong&gt;, and everything will work as before, but with the project in a different directory.&lt;/p&gt;

&lt;p&gt;Next, right-click on the solution name and &lt;strong&gt;run “Sync Namespaces.”&lt;/strong&gt;&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-12-09-RenameProjectsVisualStudio/SyncNamespaces.png&quot; alt=&quot;Visual Studio Sync Namespaces option&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Fix all namespaces with &apos;Sync Namespaces&apos; option&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next, &lt;strong&gt;use “Replace in Files” to clean leftovers&lt;/strong&gt;. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchSettings.json&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appSettings.json&lt;/code&gt; files.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-12-09-RenameProjectsVisualStudio/ReplaceInFiles.png&quot; alt=&quot;Visual Studio Replace In Files&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Fix all other leftovers with &apos;Replace In Files&apos; option&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Everything should compile. Horraaay!&lt;/p&gt;

&lt;p&gt;That was a tedious process. Especially if, like me, you have to rename all source and tests projects in a big solution.&lt;/p&gt;

&lt;p&gt;Given the amount of votes of the StackOverflow answer I followed, more than 400, I bet somebody else thought about automating this process.&lt;/p&gt;

&lt;p&gt;Indeed.&lt;/p&gt;

&lt;h2 id=&quot;2-rename-folders-and-projects-with-projectrenamer&quot;&gt;2. Rename folders and projects with ProjectRenamer&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ModernRonin/ProjectRenamer&quot;&gt;ProjectRenamer&lt;/a&gt; is a dotnet tool that does all the heavy and repetitive work for us. It renames the folders, csproj files, and project references. Also, it could create a Git commit and build the solution.&lt;/p&gt;

&lt;p&gt;ProjectRenamer expects the Git working directory to be clean. Stash or commit all other changes before using it.&lt;/p&gt;

&lt;p&gt;Using a Powershell prompt, from the folder containing the .sln file, run:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; renameproject.exe SomeThing.Core Something.Core &lt;span class=&quot;nt&quot;&gt;--no-commit&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-commit&lt;/code&gt; flag, ProjectRenamer will only stage the files. And, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-review&lt;/code&gt; skips the user confirmation. It’s like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; flag of some Unix commands.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to rename a project inside a Visual Studio solution. The painful and the quick way. Another &lt;a href=&quot;/2020/04/13/ProgramThatSave100Hours/&quot;&gt;tool that saved me like 100 hours&lt;/a&gt;. For more productivity tricks, check &lt;a href=&quot;/2020/09/02/TwoRecurringReviewComments/&quot;&gt;how to use Git to format commit messages&lt;/a&gt; and my &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;Visual Studio setup for C#&lt;/a&gt;. I never did it by hand again.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Let&apos;s refactor a test: Store and Update OAuth connections</title>
   <link href="https://canro91.github.io/2022/12/08/TestingOAuthConnections/"/>
   <updated>2022-12-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/08/TestingOAuthConnections</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last time, in the &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt;, we &lt;a href=&quot;/2021/08/02/LetsRefactorATest/&quot;&gt;refactored a unit test&lt;/a&gt; for a method that fed a report of transactions in a payment system. This time, let’s refactor another test. This test is based on a real test I had to refactor in one of my client’s projects.&lt;/p&gt;

&lt;p&gt;Before looking at our test, a bit of background. This test belongs to a two-way integration between a Property Management System and a third-party service. Let’s call it: &lt;a href=&quot;https://en.wikipedia.org/wiki/Acme_Corporation&quot;&gt;Acme Corporation&lt;/a&gt;. To connect one of our properties to Acme, we go throught an OAuth flow.&lt;/p&gt;

&lt;h2 id=&quot;a-bit-of-background-on-oauth-flows&quot;&gt;A bit of background on OAuth flows&lt;/h2&gt;

&lt;p&gt;To start the OAuth flow, we call an Authorize endpoint in a web browser. Acme prompts us to enter a user and password. Then, they return a verification code. With it, we call a Token endpoint to grab the authentication and refresh tokens. We use the authentication token in a header in future requests.&lt;/p&gt;

&lt;p&gt;Apart from the authentication and refresh codes, to make this integration work in both ways, we create some random credentials and send them to Acme. With these credentials, Acme calls some public endpoints on our side.&lt;/p&gt;

&lt;h2 id=&quot;heres-the-test-to-refactor&quot;&gt;Here’s the test to refactor&lt;/h2&gt;

&lt;p&gt;With this background, let’s look at the test we’re going to refactor. This is an integration test that checks that we can create, update and retrieve Acme “connections” in our database.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConnectionRepositoryTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AnyAuthenticationToken&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AnyRefreshToken&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeFutureExpirationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OtherAcmeCredentials&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OtherAuthenticationToken&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;OtherRefreshToken&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SomeFutureExpirationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompanyId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCompanyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAcmeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_acmeConnectionServiceMock&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAcmeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetConnectionAsync_ConnectionUpdated_ReturnsUpdatedConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnectionRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnySqlConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GeneratePkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ourCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GeneratePkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetOurCredentialsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_acmeConnectionServiceMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodeVerifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RefreshToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessTokenExpiration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yes, that’s the real test. “Some names have been changed to protect the innocent.” Can you take a look and identify what our test does?&lt;/p&gt;

&lt;p&gt;To be fair, here’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AcmeConnection&lt;/code&gt; class with the signature of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Load()&lt;/code&gt; and other methods,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LightspeedConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PmsPropertyId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PmsPropertyId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeConnectionId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeCompany&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkce&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeCredentials&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ourCredentials&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Create a new AcmeConnection from all the parameters&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// A bunch of methods to update the AcmeConnection state&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GeneratePkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateAcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;company&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetOurCredentialsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAcmeService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pkce&lt;/code&gt; object corresponds to two security codes we exchange in the OAuth flow. For more details, see &lt;a href=&quot;https://dropbox.tech/developers/pkce--what-and-why-&quot;&gt;Dropbox guide on PKCE&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1517373116369-9bdb8cdc9f62?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY3MDI1MzEyOQ&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;A electronic panel with lots of cables&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@barkiple?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;John Barkiple&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;whats-wrong&quot;&gt;What’s wrong?&lt;/h2&gt;

&lt;p&gt;Did you spot what our test does? Don’t worry. It took me some time to get what this test does, even though I was familiar with that codebase.&lt;/p&gt;

&lt;p&gt;That test is full of noise and hard to follow. It abuses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acmeConnection&lt;/code&gt; variable. It keeps reading and assigning connections to it.&lt;/p&gt;

&lt;p&gt;Behind all that noise, our test creates a new connection and stores it. Then, it retrieves, mutates, and updates the same connection. And in the last step, it recreates another one from all the input values to use it in the Assert part.&lt;/p&gt;

&lt;p&gt;Let’s see the test again, annotated this time,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetConnectionAsync_ConnectionUpdated_ReturnsUpdatedConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnectionRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnySqlConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 1. Create connection                 ^^^^^&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GeneratePkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ourCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 2. Change both credentials&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 3. Retrieve the newly created connection&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GeneratePkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//             ^^^^&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//             ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetOurCredentialsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_acmeConnectionServiceMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 4. Change Acme company and both credentials again&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 5. Update&lt;/span&gt;
    
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                              ^^^^^&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodeVerifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RefreshToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessTokenExpiration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdatedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, this test keeps using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Load()&lt;/code&gt; method, even though the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AcmeConnection&lt;/code&gt; class has some methods to update its own state.&lt;/p&gt;

&lt;h2 id=&quot;step-1-use-the-same-code-as-the-production-code&quot;&gt;Step 1. Use the same code as the Production code&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Write integration tests using the same code as the production code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s write our test in terms of our business methods instead of using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Load()&lt;/code&gt; everywhere.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetConnectionAsync_ConnectionUpdated_ReturnsUpdatedConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnectionRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnySqlConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 1. Create connection                 ^^^^^&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GeneratePkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//             ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 2. Update pkce&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//             ^^^^^&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//             ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetOurCredentialsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_acmeConnectionServiceMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 3. Update company and credentials&lt;/span&gt;
    
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatedConnectionFromDb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, we stopped using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Load()&lt;/code&gt; method. We rewrote the test using the methods from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AcmeConnection&lt;/code&gt; class like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateAcmeCredentials&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SetOurCredentialsAsync&lt;/code&gt;, and others.&lt;/p&gt;

&lt;p&gt;Also, we separated the test into blocks. In each block, we retrieved the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acmeConnection&lt;/code&gt;, mutated it with its own methods, and called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UpdateAcmeConnectionAsync()&lt;/code&gt;. Cleaner!- I’d say.&lt;/p&gt;

&lt;p&gt;We removed the last &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Load()&lt;/code&gt; call. We didn’t need to assert if the last retrieved object was exactly the same as the recreated version. Instead, we checked that the updated connection had the same value objects.&lt;/p&gt;

&lt;h2 id=&quot;step-2-use-descriptive-variables&quot;&gt;Step 2. Use descriptive variables&lt;/h2&gt;

&lt;p&gt;For the next step, let’s stop abusing the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acmeConnection&lt;/code&gt; variable and create more descriptive variables for every step.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetConnectionAsync_ConnectionUpdated_ReturnsUpdatedConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnectionRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnySqlConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newlyCreated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;newlyCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GeneratePkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newlyCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkceUpdated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pkceUpdated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pkceUpdated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherAcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkceUpdated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetOurCredentialsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_acmeConnectionServiceMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UpdateAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkceUpdated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAcmeConnectionAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acmeConnectionId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkceUpdated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkceUpdated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With these variables names is easier to follow what our test does.&lt;/p&gt;

&lt;h2 id=&quot;an-alternative-solution-with-factory-methods&quot;&gt;An alternative solution with Factory methods&lt;/h2&gt;

&lt;p&gt;We were lucky there were a lot of methods on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AcmeConnection&lt;/code&gt; class to mutate and update it in the tests. If we didn’t have those methods, we could create one “clone” method for every property we needed to mutate.&lt;/p&gt;

&lt;p&gt;For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AcmeConnectionExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CredentialsFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LightspeedConnection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCredentials&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;OurCredentials&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ourCredentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Copy self and change AcmeCredentials and OurCredentials&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AcmeConnection&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AcmeCompanyFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LightspeedConnection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AcmeCompany&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acmeCompany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Copy self and change the AcmeCompany&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can create an initial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AcmeConnection&lt;/code&gt; and clone it with our helper methods to reduce all boilerplate in our original test.&lt;/p&gt;

&lt;p&gt;Voilà! That was a long refactoring session. There are two things we can take away from this refactoring. First, we should strive for readability in our tests. We should make our test even more readable than our production code. Can anyone spot what one of our tests does in 30 seconds? That’s a readable test. Second, we should always write our tests using the same code as our production code. We shouldn’t write production code to only use it inside our unit tests. That &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Load()&lt;/code&gt; method was a backdoor to build objects when we should have used class constructors and methods to mutate its state.&lt;/p&gt;

&lt;p&gt;To read more content about unit testing, check &lt;a href=&quot;/2022/12/01/TestingHttpClient/&quot;&gt;how to write tests for HttpClient&lt;/a&gt;, &lt;a href=&quot;/2022/12/03/TestingAspNetAuthorizationFilters/&quot;&gt;how to test an ASP.NET filter&lt;/a&gt;, and &lt;a href=&quot;/2022/12/04/TestingLoggingAndLogMessages/&quot;&gt;how to write tests for logging messages&lt;/a&gt;. Don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover from naming conventions to best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I&apos;m banning Get, Set, and other method and class names</title>
   <link href="https://canro91.github.io/2022/12/07/BanningSomeNamingConventions/"/>
   <updated>2022-12-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/07/BanningSomeNamingConventions</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Names are important in programming. Good names could be the difference between a developer nodding his head in agreement or making funny faces in a “Wait, whaaaat?” moment. Names are so important that the &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt; and &lt;a href=&quot;/2021/12/20/TheArtOfReadableCodeReview/&quot;&gt;The Art of Readable Code&lt;/a&gt; devote entire chapters to the subject. These are some words I’m banning from my method and class names.&lt;/p&gt;

&lt;h2 id=&quot;1-get-and-set-in-method-names&quot;&gt;1. Get and Set in method names&lt;/h2&gt;

&lt;p&gt;I wish I could remember what Kevlin Henney’s presentation has this idea. He argues that “Get” and “Set” are some words with more meanings in an English dictionary. Then why do we use them in our code when our names should be the least ambiguous as possible? He has a point!&lt;/p&gt;

&lt;p&gt;These days I &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;reviewed a pull request&lt;/a&gt; that had a code block that reminded me about this point. It looked like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RoomCharge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ReceiptId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReceiptId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RoomId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoomId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ReservationId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReservationId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SetReservationId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReservationId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reservationId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//          ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ReservationId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reservationId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Maybe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithReservationId()&lt;/code&gt; or simply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReservationId()&lt;/code&gt; would be better alternatives. Even an old auto-implemented property would get our backs covered here.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1568630341816-3087686712dc?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2ODcyNDE0MA&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Danger do not entry sign&quot; /&gt;

&lt;figcaption&gt;Do not cross. Photo by &lt;a href=&quot;https://unsplash.com/@bailey_i?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Issy Bailey&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-utility-and-helper-classes&quot;&gt;2. Utility and Helper classes&lt;/h2&gt;

&lt;p&gt;The next names I’m banning are the “Utility” and “Helper” suffixes in class names. Every time I see them, I wonder if the author (and I) missed an opportunity to create domain entities or better named classes.&lt;/p&gt;

&lt;p&gt;In one of my client’s projects, I had to work with a class that looked like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MetadataHelper&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddFeeRates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Doing something with &apos;fee&apos; and &apos;request&apos; to populate &apos;metadata&apos;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddFeeRates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StripePaymentIntent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Doing something with &apos;fee&apos; and &apos;paymentIntent&apos; to populate &apos;metadata&apos;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It was a class that generated some payment metadata based on payment fees and requests. Somebody took the easy route and dumped everything in a static &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MetadataHelper&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Instead, we could write a non-static &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentMetadata&lt;/code&gt; class to wrap the metadata dictionary. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentMetadata&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_metadata&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddFeeRates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Doing something with &apos;fee&apos; and &apos;request&apos; to expand &apos;metadata&apos;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddFeeRates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StripePaymentIntent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Doing something with &apos;fee&apos; and &apos;paymentIntent&apos; to expand &apos;metadata&apos;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;If a concept is important inside the business domain, we should promote it out of helper classes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Often, we use Utility and Helper classes to dump all kinds of methods we couldn’t find a good place for.&lt;/p&gt;

&lt;h2 id=&quot;3-constants-classes&quot;&gt;3. Constants classes&lt;/h2&gt;

&lt;p&gt;This isn’t exactly a name. But the last thing I’m banning is Constant classes. I learned this lesson after reading &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently, I found some code that looked like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Constants&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TransactionTypeId&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoomCharge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentMethod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OtherConstant&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Anything&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnythingElse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Anything&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It was a class full of unrelated constants. Here, I only showed five of them. Among those, I found the types of transactions in a reservation management system.&lt;/p&gt;

&lt;p&gt;On the caller side, a method that expects any of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TransactionTypeId&lt;/code&gt; uses an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; parameter. For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ItUsesATransactionTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transactionTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                                   ^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; won’t work. Only those inside the Constants class are the valid ones.&lt;/p&gt;

&lt;p&gt;This gets worse when Constant classes start to proliferate, and every project of a solution has its own Constants class. Arggggg!&lt;/p&gt;

&lt;p&gt;Instead of Constants classes, let’s use enums to restrict the values we can pass to methods. Or, at least, let’s move the constants closer to where they’re expected, not in a catch-all class. With an enum, the compiler helps us to check if we are passing a “good” value.&lt;/p&gt;

&lt;p&gt;Using an enum, our previous example looks like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TransactionTypeId&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RoomCharge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PaymentMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Tax&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ItUsesATransactionTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TransactionTypeId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transactionTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                                   ^^^^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! These are the names I’m banning in my own code. And I wish I could ban them in code reviews too. Are you also guilty of any of the three? I’ve been there and done that.&lt;/p&gt;

&lt;p&gt;Speaking about names, check &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;How to name your unit tests&lt;/a&gt; to write more descriptive test names and &lt;a href=&quot;/2020/12/10/PrimitiveObsession/&quot;&gt;A real-world case of primitive obsession&lt;/a&gt; to learn to enforce business constraints and rules with domain entities.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy naming!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to replace BackgroundServices with a lite Hangfire</title>
   <link href="https://canro91.github.io/2022/12/06/BackgroundServicesAndLiteHangfire/"/>
   <updated>2022-12-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/06/BackgroundServicesAndLiteHangfire</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I like ASP.NET Core BackgroundServices. I’ve used them in one of my client’s projects to run recurring operations outside the main ASP.NET Core API site. Even for small one-time operations, I’ve run them in the same API site.&lt;/p&gt;

&lt;p&gt;There’s one catch. We have to write our own retrying, multi-threading, and reporting mechanism. BackgroundServices are a lightweight alternative to run background tasks.&lt;/p&gt;

&lt;h2 id=&quot;lite-hangfire&quot;&gt;Lite Hangfire&lt;/h2&gt;

&lt;p&gt;These days, a coworker came up with the idea to use a “lite” Hangfire to replace ASP.NET Core BackgroundServices. By “lite,” he meant an in-memory, single-thread Hangfire configuration.&lt;/p&gt;

&lt;p&gt;Let’s create an ASP.NET Core API site and install these NuGet packages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hangfire,&lt;/li&gt;
  &lt;li&gt;Hangfire.AspNetCore,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/HangfireIO/Hangfire.InMemory&quot;&gt;Hangfire.InMemory&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/pieceofsummer/Hangfire.Console&quot;&gt;Hangfire.Console&lt;/a&gt; to bring color to our lives&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;1-register-hangfire&quot;&gt;1. Register Hangfire&lt;/h3&gt;

&lt;p&gt;In the Program.cs file, let’s register the Hangfire server, dashboard, and recurring jobs. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LiteHangfire.Extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureHangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseAuthorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseHangfireDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapHangfireDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureRecurringJobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make things cleaner, let’s use extension methods to keep all Hangfire configurations in a single place. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire.Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire.InMemory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RecreatingFilterScenario.Jobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LiteHangfire.Extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServiceCollectionExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureHangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseInMemoryStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Since we have good memory&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseConsole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHangfireServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SchedulePollingInterval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// For RecurringJobs: Delay between retries.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// By default: 15sec&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkerCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Number of worker threads.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// By default: min(processor count * 5, 20)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;GlobalJobFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutomaticRetryAttribute&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Attempts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Retry count.&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// By default: 10&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureRecurringJobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//var config = app.Services.GetRequiredService&amp;lt;IOptions&amp;lt;MyRecurringJobOptions&amp;gt;&amp;gt;().Value;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// To read the cron expression from a config file&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;0/1 * * * *&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Every minute. Change it to suit your own needs&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trigger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseInMemoryStorage()&lt;/code&gt; method to store jobs in memory instead of a database and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseConsole()&lt;/code&gt; to bring color to our logging messages in the Dashboard.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1589320011103-48e428abcbae?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY3MDAxODI0Mw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Car factory&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@carlosaranda?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;carlos aranda&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;2-change-some-hangfire-parameters&quot;&gt;2. Change some Hangfire parameters&lt;/h3&gt;

&lt;p&gt;In the previous step, when we registered the Hangfire server, we used these parameters:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SchedulePollingInterval&lt;/code&gt; is the time to wait between retries for recurring jobs. By default, it’s 15 seconds. &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/blob/5b696d4174e13c3dd9489cc6a863d3417c632e31/src/Hangfire.Core/Server/RecurringJobScheduler.cs#L329&quot;&gt;Source&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WorkerCount&lt;/code&gt; is the number of processing threads. By default, it’s the minimum between five times the processor count and 20. &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/blob/5b696d4174e13c3dd9489cc6a863d3417c632e31/src/Hangfire.Core/BackgroundJobServer.cs#L171&quot;&gt;Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an aside, I also discovered these settings:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServerCheckInterval&lt;/code&gt; is how often Hangfire checks for “timed out” servers. By default, it’s 5 minutes. &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/blob/5b696d4174e13c3dd9489cc6a863d3417c632e31/src/Hangfire.Core/Server/ServerWatchdog.cs#L40&quot;&gt;Source&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServerTimeout&lt;/code&gt; is the time to consider that a server timed out from the last heartbeat. By default, it’s 5 minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, we registered the number of retry attempts. By default, Hangfire retries jobs 10 times. &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/blob/5b696d4174e13c3dd9489cc6a863d3417c632e31/src/Hangfire.Core/AutomaticRetryAttribute.cs#L83&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;3-write-producer-and-consumer-jobs&quot;&gt;3. Write “Producer” and “Consumer” jobs&lt;/h3&gt;

&lt;p&gt;The next step is to register a recurring job as a “producer.” It looks like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire.Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Hangfire.Server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LiteHangfire.Jobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProducerRecurringJob&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JobId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IBackgroundJobClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_backgroundJobClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBackgroundJobClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backgroundJobClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProducerRecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_backgroundJobClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;backgroundJobClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Running recurring job at {now}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// We could read pending jobs from a database, for example&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_backgroundJobClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkerJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomeWorkAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Inside this recurring job, we can read pending jobs from a database and enqueue a new worker job for every pending job available.&lt;/p&gt;

&lt;p&gt;And a sample worker job that uses Hangfire.Console looks like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WorkerJob&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomeWorkAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PerformContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetTextColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConsoleTextColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Doing some work at {0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UtcNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that we expect a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PerformContext&lt;/code&gt; as a parameter to change the color of the logging message. When we enqueued the worker jobs, we passed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; as context, then Hangfire uses the right instance when running our jobs. &lt;a href=&quot;https://stackoverflow.com/questions/38368153/how-do-i-get-the-current-attempt-number-on-a-background-job-in-hangfire/38387512&quot;&gt;Source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to use a lite Hangfire to replace BackgroundServices without adding too much overhead or a new database to store jobs. With the advantage that Hangfire has recurring jobs, retries, and a Dashboard out of the box.&lt;/p&gt;

&lt;p&gt;After solving a couple of issues, I learned some &lt;a href=&quot;/2022/12/13/LessonsOnHangfireAndOrmLite/&quot;&gt;lessons when working with Hangfire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To read more content about ASP.NET Core, check &lt;a href=&quot;/2022/12/01/TestingHttpClient/&quot;&gt;how to write tests for HttpClient&lt;/a&gt; and &lt;a href=&quot;/2022/12/03/TestingAspNetAuthorizationFilters/&quot;&gt;how to test an ASP.NET filter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>I don&apos;t use &apos;Pushy&apos; questions in code reviews anymore. This is what I do instead</title>
   <link href="https://canro91.github.io/2022/12/05/LeadingQuestionsOnCodeReviews/"/>
   <updated>2022-12-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/05/LeadingQuestionsOnCodeReviews</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;“Ask questions” is common advice for better code reviews.&lt;/p&gt;

&lt;p&gt;At some point, we followed that advice and started using what I call “leading” or “pushy” questions. Questions that only hint a request for a code change.&lt;/p&gt;

&lt;p&gt;After working on a remote software team for a couple years, I stopped using “pushy” questions on code reviews. Here’s why it’s a bad idea.&lt;/p&gt;

&lt;h2 id=&quot;pushy-questions-are-time-consuming&quot;&gt;“Pushy” Questions Are Time-Consuming&lt;/h2&gt;

&lt;p&gt;Let’s imagine we’ve written a method and forgot to check for nulls. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OneParam&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oneParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnotherParam&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anotherParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AMethodThatUsesOneParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oneParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we follow the advice to ask “pushy” questions, we might leave and receive comments like &lt;em&gt;“What if oneParam is null?”&lt;/em&gt; or &lt;em&gt;“Could oneParam or anotherParam be null?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The problem with those types of comments is we can’t tell if they’re genuine questions or actionable items. Is the reviewer asking a clarification question or “pushing” us in a different direction? We can’t tell.&lt;/p&gt;

&lt;p&gt;Behind those comments, there’s a hidden change request. How is the code author supposed to know the reviewer is asking for a change?&lt;/p&gt;

&lt;p&gt;While working on a remote team, it happened more than once that I had to reach out to reviewers via email or chat to ask them to clarify their intentions behind those comments. But some reviewers were in different time zones or even on the other side of the world. All interactions took about ~24 hours between my initial message and their response.&lt;/p&gt;

&lt;p&gt;It was frustrating and time-consuming. Arrrggg!&lt;/p&gt;

&lt;p&gt;When it was my turn to be a code reviewer, I chose a different approach: I stopped asking those questions.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1620662736427-b8a198f52a4d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2ODcyMzYyMA&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;The Thinker&quot; /&gt;

&lt;figcaption&gt;That&apos;s a tricky question. Let me think about it. Photo by &lt;a href=&quot;https://unsplash.com/@tingeyinjurylawfirm?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Tingey Injury Law Firm&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/question?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;use-unambiguous-and-intentional-comments&quot;&gt;Use Unambiguous and Intentional Comments&lt;/h2&gt;

&lt;p&gt;Instead of asking “pushy” questions, let’s leave actionable and unambiguous comments that distinguish between questions, to-dos, and nice-to-haves.&lt;/p&gt;

&lt;p&gt;Let’s go back to the previous example and leave an unambiguous comment. Like this one: &lt;em&gt;“Is it possible that oneParam could be null? If that’s the case, please let’s add the appropriate null checks. Something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (oneParam == null) throw ...&lt;/code&gt;“&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that comment, it’s clear we’re suggesting a change.&lt;/p&gt;

&lt;p&gt;To better show the intention behind our comments, we can use &lt;a href=&quot;https://conventionalcomments.org/&quot;&gt;Conventional Comments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With that convention, we add keywords like “question,” “suggestion” or “nitpick” to clarify the purpose of our comments.&lt;/p&gt;

&lt;p&gt;I used it for months in one of my client’s projects and other reviewers started to use it too.&lt;/p&gt;

&lt;p&gt;For example, we can turn our previous “pushy” comment into these two depending on our intention:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A clarification question: &lt;em&gt;“&lt;strong&gt;question:&lt;/strong&gt; Is it possible that oneParam could be null?”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;A change request: &lt;em&gt;“&lt;strong&gt;suggestion (blocking):&lt;/strong&gt; Let’s add the appropriate null checks if oneParam could be null.”&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now it’s clear we’re referring to two different actions.&lt;/p&gt;

&lt;p&gt;Voilà! That’s why I don’t like “pushy” questions in code reviews. Let’s always prefer clear and direct comments without forgetting good manners of course. And let’s remember we review code from people with different experience levels and even non-native speakers of our language.&lt;/p&gt;

&lt;p&gt;After this experience, my rule of thumb for better code reviews is to write unambiguous comments and always include a suggestion with each comment.&lt;/p&gt;

&lt;p&gt;If you want to read more about code reviews, check these &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;Tips and Tricks for Better Code Reviews&lt;/a&gt; and these &lt;a href=&quot;/2022/12/19/LessonsAsReviewer/&quot;&gt;lessons I learned about as code reviewer&lt;/a&gt;. And if you’re interested in unit testing, this lesson came up during  a code review session: &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;how to use simple test values to write good unit tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>On Unit Testing Logging Messages</title>
   <link href="https://canro91.github.io/2022/12/04/TestingLoggingAndLogMessages/"/>
   <updated>2022-12-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/04/TestingLoggingAndLogMessages</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I had to review some code that expected a controller to log the exceptions thrown in a service. This is how that controller looked and what I learned about testing logging messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When writing unit tests for logging, assert that actual log messages contain keywords like identifiers or requested values. Don’t assert that actual and expected log messages are exactly the same.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;dont-expect-identical-log-messages&quot;&gt;Don’t expect identical log messages&lt;/h2&gt;

&lt;p&gt;The controller I reviewed looked like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[controller]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerBase&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IClientService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IClientService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                               &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_clientService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpPost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPostRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Imagine that this service does something interesting...&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_clientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Something horribly wrong happened. ClientId: [{clientId}]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//      ^^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Logging things like good citizens of the world...&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BadRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Other methods here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nothing fancy. It called an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IClientService&lt;/code&gt; service and logged the exception thrown by it. Let’s imagine that the controller logged a more helpful message to troubleshoot later. I wrote a funny log message here. Yes, exception filters are a better idea, but bear with me.&lt;/p&gt;

&lt;p&gt;To test if the controller logs exceptions, we could write a unit test like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingControllerTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PostAsync_Exception_LogsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IClientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Expected exception...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 3...2...1...Boom...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                       ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyPostRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Something horribly wrong happened. ClientId: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  ^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We expect exactly the same log message from the PostAsync&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyWasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In that test, we used &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;Moq to create fakes&lt;/a&gt; for our dependencies, even for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ILogger&lt;/code&gt; itself. I prefer to call them fakes. There’s a &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;difference between stubs and mocks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By the way, &lt;a href=&quot;/2024/04/01/NET8FakeLogger/&quot;&gt;.NET 8.0 added a FakeLogger&lt;/a&gt;, a logging provider for unit testing, so we don’t have to rely on fakes to test logging.&lt;/p&gt;

&lt;p&gt;In our test, we’re expecting the actual log message to be exactly the same as the one from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SomethingController&lt;/code&gt;. Can you already spot the duplication? In fact, we’re rebuilding the log message inside our tests. We’re &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;duplicating the logic under test&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, let’s notice we used a &lt;a href=&quot;/2021/08/16/WriteCustomAssertions/&quot;&gt;custom assertion method&lt;/a&gt; to make our assertions less verbose. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyWasCalled()&lt;/code&gt; is an extension method that inspects the Moq instance to check if the actual and expected messages are equal. Here it is,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoqExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VerifyWasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAnyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAnyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;()),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1527190074017-f32101b5d57b?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2ODcyMzM5Nw&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Pile of tree logs&quot; /&gt;

&lt;figcaption&gt;Don&apos;t expect identical log...messages. Photo by &lt;a href=&quot;https://unsplash.com/es/@chanphoto?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Chandler Cruttenden&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/log?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;instead-expect-log-messages-to-contain-keywords&quot;&gt;Instead, expect log messages to contain keywords&lt;/h2&gt;

&lt;p&gt;To make our unit tests more maintainable, let’s check that log messages contain keywords or relevant substrings, like identifiers and values from input requests. Let’s not check if they’re identical to the expected log messages. Any changes in casing, punctuation, spelling or any other minor changes in the message structure will make our tests break.&lt;/p&gt;

&lt;p&gt;Let’s rewrite our test,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;OnTestingLogMessages.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingControllerTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PostAsync_Exception_LogsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IClientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Expected exception...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//           ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 3...2...1...Boom...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeClientService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyPostRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyMessageContains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//         ^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We expect the same log message to only contain the clientId&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we rolled another extension method, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyMessageContains()&lt;/code&gt;,  removed the expected log message and asserted that the log message only contained only relevant subtrings: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clientId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here it is the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyMessageContains()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoqExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VerifyMessageContains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAnyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))),&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// Checking if the log message contains some keywords, instead&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAnyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;()),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how to make our test that checks logging messages more maintainable. By not rebuilding log messages inside tests and asserting that they contain keywords instead of expecting to be exact matches.&lt;/p&gt;

&lt;p&gt;Here we dealt with logging for diagnostic purposes (logging to make troubleshooting easier for developers). But if logging were a business requirement, we should have to make it a separate “concept” in our code. Not in logging statements scatter all over the place. I learned by distinction about logging when reading &lt;a href=&quot;/2022/10/17/UnitTestingPrinciplesPracticesTakeaways/&quot;&gt;Unit Testing Principles, Practices, and Patterns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to read more about unit testing, check &lt;a href=&quot;/2022/12/01/TestingHttpClient/&quot;&gt;How to write tests for HttpClient using Moq&lt;/a&gt;, &lt;a href=&quot;/2022/12/03/TestingAspNetAuthorizationFilters/&quot;&gt;How to test an ASP.NET Authorization Filter&lt;/a&gt; and my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where we cover from what a unit test is, to fakes and mocks, to best practices.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to test an ASP.NET Authorization Filter</title>
   <link href="https://canro91.github.io/2022/12/03/TestingAspNetAuthorizationFilters/"/>
   <updated>2022-12-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/03/TestingAspNetAuthorizationFilters</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I needed to work with a microservice for one of my clients. In that microservice, instead of validating incoming requests with the built-in model validations or FluentValidation, they use authorization filters. I needed to write some tests for that filter. This is what I learned.&lt;/p&gt;

&lt;p&gt;Apart from validating the integrity of the incoming requests, the filter also validated that the referenced object in the request body matched the same “client.”&lt;/p&gt;

&lt;h2 id=&quot;a-weird-filter-scenario&quot;&gt;A weird filter scenario&lt;/h2&gt;

&lt;p&gt;The filter looked something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc.Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyWeirdFilterScenario.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyWeirdFilterScenario.Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAuthorizationFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAsyncAuthorizationFilter&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationFilterContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_validationsPerEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IClientRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_clientRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IOtherEntityRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_otherEntityRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyAuthorizationFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IClientRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                 &lt;span class=&quot;n&quot;&gt;IOtherEntityRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherEntityRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_clientRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_otherEntityRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherEntityRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Register validations per action name here&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// vvvvv&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_validationsPerEndpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationFilterContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringComparer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;ValidatePostAsync&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Register validations for other methods here...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnAuthorizationAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationFilterContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ControllerActionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_validationsPerEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isValid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//                  ^^^^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Grab and run the validation for the called endpoint&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BadRequestResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Log bad things here...&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BadRequestResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ValidatePostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationFilterContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetRequestBodyAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPostRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                  ^^^^^^^^^^^^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Grab the request body&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_clientRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  ^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Check our client exists...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherEntity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_otherEntityRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OtherEntityId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;otherEntity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherEntity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  ^^^^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Check we&apos;re updating our own entity...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Doing something else here...&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// A helper method to grab the request body from the AuthorizationFilterContext&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetRequestBodyAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AuthorizationFilterContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnableBuffering&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestBodyJson&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadToEndAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestBodyJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializerSettings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NullValueHandling&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NullValueHandling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ignore&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestBody&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeserializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestBodyJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnAuthorizationAsync()&lt;/code&gt; method, this filter grabbed the validation method based on the called method name. And, inside the validation method, it checked that the request had a valid “clientId” and the referenced entity belonged to the same client. This is to prevent any client from updating somebody else’s entities.&lt;/p&gt;

&lt;p&gt;Also, notice we needed to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnableBuffering()&lt;/code&gt; and reset the body’s position before and after reading the body from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AuthorizationFilterContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the controller side, we registered the filter with an attribute like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RecreatingFilterScenario.Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyAuthorizationFilter.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[controller]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ServiceFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyAuthorizationFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                    ^^^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerBase&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpPost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPostRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Doing something with request&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Other methods here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And, to make it work, we also need to register our filter in the dependencies container.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1585047209652-ab8537bf6d6d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2ODcyMzIzNg&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Morning Brew&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@krsp?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Kris Gerhard&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/coffee-filter?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;how-to-test-an-aspnet-async-authorization-filter&quot;&gt;How to test an ASP.NET async authorization filter&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;To test an ASP.NET async filter, create a new instance of the filter passing the needed dependencies as stubs. Then, when calling the OnAuthorizationAsync() method, create a AuthorizationFilterContext instance attaching the request body inside a DefaultHttpContext.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc.Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Routing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RecreatingFilterScenario.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RecreatingFilterScenario.Filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyWeirdFilterScenario.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAuthorizationFilterTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnAuthorizationAsync_OtherEntityWithoutTheSameClient_ReturnsBadRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sameClientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherClientId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherEntityId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClientRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IClientRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClientRepository&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sameClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sameClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeOtherEntityRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOtherEntityRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeOtherEntityRepository&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetByIdAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;otherEntityId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OtherEntity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;otherClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyAuthorizationFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeClientRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeOtherEntityRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  ^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Create an instance of our filter with two fake dependencies&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyPostRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sameClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherEntityId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Create an AuthorizationFilterContext&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OnAuthorizationAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BadRequestResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AuthorizationFilterContext&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPostRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DefaultHttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MemoryStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetBytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;httpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;httpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentLength&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;httpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Attach a JSON body&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionDescriptor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerActionDescriptor&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ActionName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomethingController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Use the endpoint name&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ActionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RouteData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AuthorizationFilterContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actionContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFilterMetadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s unwrap it. First, we created an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyAuthorizationFilter&lt;/code&gt; passing the dependencies as &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;fakes using Moq&lt;/a&gt;. As stubs, to be precise.&lt;/p&gt;

&lt;p&gt;To call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnAuthorizationAsync()&lt;/code&gt; method, we needed to create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AuthorizationFilterContext&lt;/code&gt;. This context required an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionContext&lt;/code&gt;. We used a Builder method, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildContext()&lt;/code&gt;, to keep things clean.&lt;/p&gt;

&lt;p&gt;Then, to create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionContext&lt;/code&gt;, we needed to attach the request body as JSON to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DefaultHttpContext&lt;/code&gt; and set the action descriptor with our method name. Since we didn’t read any route information, we passed a default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RouteData&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;Notice that we needed to use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MemoryStream&lt;/code&gt; to pass our request object as JSON and set the content length and type. &lt;a href=&quot;https://stackoverflow.com/questions/65076535/unit-test-middleware-how-to-add-a-httprequest-to-a-httpcontext-in-net-core-3-1&quot;&gt;Source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BuildContext()&lt;/code&gt; method in place, we got the Arrange and Act parts of our sample test. The next step was to assert on the context result.&lt;/p&gt;

&lt;p&gt;Voilà! That’s what I learned about unit testing ASP.NET authorization filters. Again, a Builder method helped to keep things simple and easier to reuse.&lt;/p&gt;

&lt;p&gt;If you want to read more about unit testing, check &lt;a href=&quot;% post_url 2022-12-01-TestingHttpClient %&quot;&gt;How to write tests for HttpClient using Moq&lt;/a&gt; and my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where we cover from what a unit test is, to fakes and mocks, to best practices.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Always check for missing configuration values inside constructors</title>
   <link href="https://canro91.github.io/2022/12/02/ValidateInputParameters/"/>
   <updated>2022-12-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/02/ValidateInputParameters</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code 2022&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a lesson I learned after trying to use a shared NuGet package in one of my client’s projects and getting an ArgumentNullException. I had no clue that I needed some configuration values in my appsettings.json file. This is what I learned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always check for missing configuration values inside constructors. In case they’re not set, throw a human-friendly exception message showing the name of the expected configuration value. For example: ‘Missing Section:Subsection:Value in config file’.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-missing-configuration-value&quot;&gt;A missing configuration value&lt;/h2&gt;

&lt;p&gt;This is what happened. I needed to import a feature from a shared Nuget package. It had a method to register its dependencies. Something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;services.AddFeature()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When calling an API endpoint that used that feature, I got an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt;: &lt;em&gt;“Value cannot be null. (Parameter ‘uriString’).”&lt;/em&gt; It seemed that I was missing a URI. But what URI?&lt;/p&gt;

&lt;p&gt;Without any XML docstrings on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddFeature()&lt;/code&gt; method, I had no other solution than to decompile that DLL. I found a service like this one,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomeService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISomeService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_anyUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyConfigOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OtherParam&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_anyUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyConfigValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                ^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// System.ArgumentNullException: Value cannot be null. (Parameter &apos;uriString&apos;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Doing something here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There it was! The service used the &lt;a href=&quot;/2020/08/21/HowToConfigureValues/&quot;&gt;IOptions pattern to read configuration values&lt;/a&gt;. And I needed an URL inside a section in the appsettings.json file. How was I supposed to know?&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1611329857570-f02f340e7378?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2ODcyMzAwOQ&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Black and brown jigsaw puzzle&quot; /&gt;

&lt;figcaption&gt;Missing one value... Photo by &lt;a href=&quot;https://unsplash.com/@sigmund?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Sigmund&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;a-better-exception-message&quot;&gt;A better exception message&lt;/h2&gt;

&lt;p&gt;Then I realized that a validation inside the constructor with a human-friendly message would have saved me (and any other future developer using that NuGet package) some time. And it would have pointed me in the right direction. I mean having something like,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomeService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISomeService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_anyUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyConfigOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OtherParam&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;otherParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//  vvvvvvv&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyConfigValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Missing &apos;AnyConfigOptions:AnyConfigValue&apos; in config file.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//                              ^^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// I think this would be a better message&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_anyUri&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyConfigValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop...&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Doing something here again...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even better, what if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddFeature()&lt;/code&gt; method had an overload that receives the expected configuration value? Something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddFeature(AnyConfigOptions options)&lt;/code&gt;. This way, the client of that package could decide the source of those options. Either read them from a configuration file or hardcode them.&lt;/p&gt;

&lt;p&gt;The book “Growing Object-Oriented Software Guided by Tests” suggests having a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StupidProgrammerMistakeException&lt;/code&gt; or a specific exception for this type of scenario: missing configuration values. This would be a good use case for that exception type.&lt;/p&gt;

&lt;p&gt;Voilà! That’s what I learned today: always validate configuration values inside constructors and use explicit error messages when implementing the Options pattern. It reminded me of “The given key was not present in the dictionary” and other obscure error messages. Do you write friendly and clear error messages?&lt;/p&gt;

&lt;p&gt;To read more content about ASP.NET Core, check &lt;a href=&quot;/2020/06/29/HowToAddACacheLayer/&quot;&gt;how to add caching with Redis&lt;/a&gt; and &lt;a href=&quot;/2020/08/21/HowToConfigureValues/&quot;&gt;how to read configuration values&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to write tests for HttpClient using Moq</title>
   <link href="https://canro91.github.io/2022/12/01/TestingHttpClient/"/>
   <updated>2022-12-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/12/01/TestingHttpClient</id>
   <content type="html">&lt;p&gt;&lt;em&gt;This post is part of &lt;a href=&quot;/AdventOfCode2022&quot;&gt;my Advent of Code&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These days I needed to unit test a service that used the built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt;. It wasn’t as easy as creating a fake for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt;. This is how to write tests for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt; with Moq and a set of extension methods to make it easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write tests for a service that requires a HttpClient, create a fake for HttpMessageHandler and set up the protected SendAsync() method to return a HttpResponseMessage. Then, create a new HttpClient passing the fake instance of HttpMessageHandler created before.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-create-a-testable-httpclient&quot;&gt;How to Create a Testable HttpClient&lt;/h2&gt;

&lt;p&gt;For example, let’s write a test for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyService&lt;/code&gt; class that receives a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt;, using MSTest and Moq,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq.Protected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Net.Http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyProject.Services.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AnyServiceTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync_ByDefault_ReturnsSomethingElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Protected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// ^^^^^^^&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpResponseMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;SendAsync&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;ItExpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpRequestMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;ItExpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseMessage&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;StatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyResponseViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// We add the expected response here:                   ^^^^^&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                    ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Assert something here...&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Protected()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Setup()&lt;/code&gt; methods from &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;Moq to create a fake&lt;/a&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HtttpMessageHandler&lt;/code&gt;. Then, inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReturnsAsync()&lt;/code&gt; method, we created a response message with a response object. And, finally, we used the fake handler to create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt; to pass it to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnyService&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;That’s how we created a fake &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt;. But, as soon as we start to write more tests, all of them get bloated with lots of duplicated code. Especially, if we create new tests by copy-pasting an existing one.&lt;/p&gt;

&lt;p&gt;We should &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;reduce the noise in our tests&lt;/a&gt; using factory methods or builders to make our tests more readable. Let’s do that!&lt;/p&gt;

&lt;h2 id=&quot;some-extensions-methods-to-set-up-the-faked-httpclient&quot;&gt;Some extensions methods to set up the faked HttpClient&lt;/h2&gt;

&lt;p&gt;It would be great if we could reduce &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;the Arrange phase&lt;/a&gt; of our sample test to one or two lines. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync_ByDefault_ReturnsSomethingElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithSuccessfulResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyResponseViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                  &lt;span class=&quot;c1&quot;&gt;//                      ^^^^^&lt;/span&gt;
                  &lt;span class=&quot;c1&quot;&gt;// Alternatively,&lt;/span&gt;
                  &lt;span class=&quot;c1&quot;&gt;// .WithUnauthorizedResponse()&lt;/span&gt;
                  &lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
                  &lt;span class=&quot;c1&quot;&gt;// .WithException&amp;lt;HttpRequestException&amp;gt;()&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToHttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AnyService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Assert something here...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s not that difficult to write some extension methods on top of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mock&amp;lt;HttpMessageHandler&amp;gt;&lt;/code&gt; to simplify the creation of testable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt; instances.&lt;/p&gt;

&lt;p&gt;In fact, here they are,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq.Language.Flow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq.Protected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Net.Http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HttpMessageHandlerTests.Extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MockHttpMessageHandlerExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithSuccessfulResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProtectedSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseMessage&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;StatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WithUnauthorizedResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProtectedSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseMessage&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;StatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unauthorized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;RequestMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpRequestMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WithDelegate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpRequestMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProtectedSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReturnsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProtectedSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Throws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToHttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpResponseMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetProtectedSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeHttpMessageHandler&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Protected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpResponseMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;SendAsync&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ItExpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpRequestMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ItExpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can add other methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithNotFoundResponse()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithInternalServerResponse()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithTooManyRequestsResponse()&lt;/code&gt; to cover other response codes. Even, we can setup the fake &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpMessageHandler&lt;/code&gt; passing an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Uri&lt;/code&gt; with a method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForUri()&lt;/code&gt;, for example.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to write tests with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt; and Moq. With some extension methods, we could have a small DSL to write more readable tests. For a more fully-featured alternative to write tests for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt;, check &lt;a href=&quot;https://github.com/richardszalay/mockhttp&quot;&gt;mockhttp&lt;/a&gt;, &lt;em&gt;“a testing layer for Microsoft’s HttpClient library.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you want to read more about unit testing, check my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where we cover from what a unit test is, to fakes and mocks, to best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advent of Code 2022</title>
   <link href="https://canro91.github.io/AdventOfCode2022"/>
   <updated>2022-12-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/AdventOfCode2022</id>
   <content type="html">&lt;p&gt;This year, inspired by &lt;a href=&quot;https://csadvent.christmas/about&quot;&gt;C# Advent&lt;/a&gt; and &lt;a href=&quot;https://24pullrequests.com/&quot;&gt;24 Pull Requests&lt;/a&gt;, I decided to do my own Christmas challenge: my own Advent of Code. I prefer to call it: Advent of Posts. Starting on December 1st, I’m publishing 24 posts, one post per day.&lt;/p&gt;

&lt;p&gt;The challenge is to write an article per day in about 2 hours, including proof-reading and banner design. I’ve written some of the post in advance to avoid content pressure.&lt;/p&gt;

&lt;p&gt;Here is my Advent of Posts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Day 1: &lt;a href=&quot;/2022/12/01/TestingHttpClient/&quot;&gt;How to write tests for HttpClient using Moq&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 2: &lt;a href=&quot;/2022/12/02/ValidateInputParameters/&quot;&gt;TIL: Always check for missing configuration values inside constructors&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 3: &lt;a href=&quot;/2022/12/03/TestingAspNetAuthorizationFilters/&quot;&gt;TIL: How to test an ASP.NET Core Authorization filter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 4: &lt;a href=&quot;/2022/12/04/TestingLoggingAndLogMessages/&quot;&gt;On Unit Testing Logging and Log Messages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 5: &lt;a href=&quot;/2022/12/05/LeadingQuestionsOnCodeReviews/&quot;&gt;I stopped using leading or tricky questions in Code Reviews&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 6: &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;How to replace BackgroundServices with a lite Hangfire&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 7: &lt;a href=&quot;/2022/12/07/BanningSomeNamingConventions/&quot;&gt;I’m banning Get, Set, and other method and class names&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 8: &lt;a href=&quot;/2022/12/08/TestingOAuthConnections/&quot;&gt;Let’s refactor a test: Store OAuth connections&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 9: &lt;a href=&quot;/2022/12/09/RenameProjectsVisualStudio/&quot;&gt;How to rename Visual Studio projects and folders with Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 10: &lt;a href=&quot;/2022/12/10/ReplaceKeywordInFile/&quot;&gt;TIL: How to replace keywords in a file name and content with Bash&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 11: &lt;a href=&quot;/2022/12/11/AuditFieldsWithOrmLite/&quot;&gt;TIL: How to automatically insert and update audit fields with OrmLite&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 12: &lt;a href=&quot;/2022/12/12/ThingsToKnowBeforeBeingSoftwareEngineer/&quot;&gt;Four things I wished I knew before becoming a software engineer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 13: &lt;a href=&quot;/2022/12/13/LessonsOnHangfireAndOrmLite/&quot;&gt;TIL: Five lessons I learned after working with Hangfire and Ormlite&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 14: &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;How to write good unit tests: Use simple test values&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 15: &lt;a href=&quot;/2022/12/15/CreateProjectStructureWithDotNetCli/&quot;&gt;How to create ASP.NET Core Api project structure with dotnet cli&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 16: &lt;a href=&quot;/2022/12/16/HelperMethodsOnCollections/&quot;&gt;Six helpful extension methods I use to work with Collections&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 17: &lt;a href=&quot;/2022/12/17/LessonsOnAFailedProject/&quot;&gt;Three lessons I learned after a “failed” project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 18: &lt;a href=&quot;/2022/12/18/LessonsFromExCoworkers/&quot;&gt;Lessons I learned from my ex-coworkers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 19: &lt;a href=&quot;/2022/12/19/LessonsAsReviewer/&quot;&gt;Lessons I learned as a code reviewer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 20: &lt;a href=&quot;/2022/12/20/SquashOldMigrations/&quot;&gt;Dump and Load to squash old migrations&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 21: &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;To Value Object or Not To: How I choose Value Objects&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 22: &lt;a href=&quot;/2022/12/22/TestingDuplicatedEmails/&quot;&gt;Let’s refactor a test: Remove duplicated emails&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Day 23: -&lt;/li&gt;
  &lt;li&gt;Day 24: -&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: 40-year Programmer, Work and Burnout</title>
   <link href="https://canro91.github.io/2022/11/21/MondayLinks/"/>
   <updated>2022-11-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/11/21/MondayLinks</id>
   <content type="html">&lt;p&gt;Another five interesting links I found in past weeks. This time, I found a recurring theme while reading Hacker News: burnout, caring and statisfaction.&lt;/p&gt;

&lt;h2 id=&quot;the-forty-year-programmer&quot;&gt;The Forty-Year Programmer&lt;/h2&gt;

&lt;p&gt;This post contains all the insights of &lt;del&gt;40-years&lt;/del&gt; a lifetime working as a programmer. These are some of my favorites:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;“Advice is expertise with all the most important bits removed.”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“The work is good. If it stops being good, you’ll stop too. If it stops being good, that’s an emergency: you need to take a vacation…“&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;“Don’t confuse work with your career. They’re not the same thing. They’re only barely related.”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://codefol.io/posts/the-forty-year-programmer/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;developers-dont-fight-the-last-war&quot;&gt;Developers Don’t Fight The Last War&lt;/h2&gt;

&lt;p&gt;This post reminded me about an ex-coworker that says that using the same skills, architecture, and tools is like being an architect who always design the same house over and over. What worked yesterady won’t work tomorrow, I guess.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“…Technology is changing all the time, but developers like to create software with the knowledge and skills they already have.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://itnext.io/developers-dont-fight-the-last-war-d7d797ae172d&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-communicate-effectively-as-a-developer&quot;&gt;How to communicate effectively as a developer&lt;/h2&gt;

&lt;p&gt;Apart from social interaction, this is another challenge when working remotely. In the old days, we just tapped somebody else’s shoulders. These days of remote and asynchronous working, communication is more challenging. And we’re not teaching that when onboarding new hires. I still get plain “Hello” messages. I just ignore them.&lt;/p&gt;

&lt;p&gt;This article shows a solution: “high resolution writing.” But, there’s another challenge with that. Often, we work with non-native speakers of the language used at work (pretty much, English everywhere). And, writing is a separate skill to master. Even for native speakers writing in his native language.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.karlsutt.com/articles/communicating-effectively-as-a-developer/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;caring-about-jobs-and-enjoyment-after-burnout&quot;&gt;Caring about jobs and enjoyment after burnout&lt;/h2&gt;

&lt;p&gt;I’ve seen an increasing amount of Hacker News posts about burnout and job-related problems. These are some of them:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=32954452&quot;&gt;Tips to relearn how to care about my job?&lt;/a&gt;: &lt;em&gt;“I got a high paying job at a recognizable tech company, but plagued with exhaustion and lack of motivation, which is killing me daily life. Anything you guys have done to dig out of a rut?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=33260525&quot;&gt;Has anyone managed to find enjoyment in their work after burnout?&lt;/a&gt;: &lt;em&gt;“Has anyone come back from being burnt out to love what they do again? If so, how did you manage to do it?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=33434742&quot;&gt;How to deal with burnout and its consequences?&lt;/a&gt;: Speaking about burnout: &lt;em&gt;“I really don’t know how to get over this and how to move past it. I feel quite literally incapable of working…I’m trying to figure out what my future even looks like and how to move past this and any advice would be really appreciated.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A lot of good advice in there.&lt;/p&gt;

&lt;h2 id=&quot;what-work-looks-like&quot;&gt;What “Work” Looks Like&lt;/h2&gt;

&lt;p&gt;I think we’ve all been to those meetings where nobody cares or even listens to what the organizer says. Especially, those where the organizer reads a document, he could have shared in the first place. This article shows an alternative to collaborative brainstorming. Spoiler alert: it’s away from computers. &lt;a href=&quot;https://blog.jim-nielsen.com/2022/what-work-looks-like/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links. Have you experience burnout? How did you overcome it? What’s different about work after burnout? What are you doing to prevent burnout?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unit Testing Principles, Practices, and Patterns: Takeaways</title>
   <link href="https://canro91.github.io/2022/10/17/UnitTestingPrinciplesPracticesTakeaways/"/>
   <updated>2022-10-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/10/17/UnitTestingPrinciplesPracticesTakeaways</id>
   <content type="html">&lt;p&gt;This book won’t teach you how to write a unit test step by step. But, it will teach you how unit testing fits the larger picture of a software project. Also, this book shows how to write integration tests and test the database. These are my takeaways.&lt;/p&gt;

&lt;h2 id=&quot;1-what-is-a-unit-test&quot;&gt;1. What is a unit test?&lt;/h2&gt;

&lt;p&gt;“The goal of unit testing is to enable sustainable growth of the software project.” “It’s easy to fall into the trap of writing unit tests for the sake of unit testing without a clear picture of whether it helps the project.”&lt;/p&gt;

&lt;p&gt;A successful test suite has the following properties:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It’s integrated into the development cycle,&lt;/li&gt;
  &lt;li&gt;It targets only the most important parts of your codebase: the domain model,&lt;/li&gt;
  &lt;li&gt;It provides maximum value with minimum maintenance costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A unit test is an automated test with three attributes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It verifies a small portion of behavior (a unit),&lt;/li&gt;
  &lt;li&gt;does it quickly, and,&lt;/li&gt;
  &lt;li&gt;in isolation from other tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are two groups of developers with different views about “isolation”: the London school and the Classical school.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;the London school&lt;/strong&gt;, isolation means writing separate tests for separate classes. If a class has collaborators, we should test it using test doubles for every collaborator.&lt;/p&gt;

&lt;p&gt;On the other hand, for &lt;strong&gt;the Classical school&lt;/strong&gt;, it’s not the code that needs to be tested in isolation, but the tests. They should run in isolation from each other. It’s ok to test more than one class at a time if the tests don’t affect others by sharing state.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“A test—whether a unit test or an integration test—should be a simple sequence of steps with no branching”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good unit test has these four attributes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Protection against regressions&lt;/strong&gt;: “Code that represents complex business logic is more important than boilerplate code.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resistance to refactoring&lt;/strong&gt;: “The more the test is coupled to the implementation details of the system under test (SUT), the more false alarms it generates.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fast feedback&lt;/strong&gt;: “The faster the tests, the more of them you can have in the suite and the more often you can run them.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: “How hard is to read a test” and “how hard is to run a test.”&lt;/li&gt;
&lt;/ol&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1537090357686-51aaa968f2ab?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2NDIzNzg1Ng&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Car cashed into a wall&quot; /&gt;

&lt;figcaption&gt;That&apos;s a failing test. Photo by &lt;a href=&quot;https://unsplash.com/@gareth_harrison?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Gareth Harrison&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-what-code-to-test&quot;&gt;2. What code to test?&lt;/h2&gt;

&lt;p&gt;Not all code is created equal and worth the same.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-10-17-UnitTestingPrinciplesPracticesTakeaways/TypesOfCode.png&quot; alt=&quot;Types of Code based on complexity and number of dependencies&quot; width=&quot;350px&quot; /&gt;
    &lt;figcaption&gt;Types of Code by Complexity and Number of Dependencies&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;There are four types of code:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Domain logic and algorithms&lt;/strong&gt;: Complex code by nature&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Trivial code&lt;/strong&gt;: Constructors without parameters and one-line properties&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Controllers&lt;/strong&gt;: Code with no business logic that coordinates other pieces&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Overcomplicated code&lt;/strong&gt;: Complex code with too many dependencies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Write unit tests for your domain model and algorithms. It gives you the best return for your efforts. Don’t test trivial code. Those tests have a close-to-zero value.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Your goal is a test suite where each test adds significant value to the project. Refactor or get rid of all other tests. Don’t allow them to inflate the size of your test suite”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;3-what-is-an-integration-test-and-how-to-test-the-database&quot;&gt;3. What is an integration test? And how to test the database?&lt;/h2&gt;

&lt;p&gt;An integration test is any test that is not a unit test. In the sense of verifying a single behavior, doing it quickly and in isolation from other tests.&lt;/p&gt;

&lt;p&gt;Write integration tests to cover the longest happy path and use the same code that the “controllers” use.&lt;/p&gt;

&lt;p&gt;Before writing integration tests for the database:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Keep the database in the source control system. It keeps track of changes and makes the code the single source of truth&lt;/li&gt;
  &lt;li&gt;Make reference data part of the database schema&lt;/li&gt;
  &lt;li&gt;Have every developer roll a separate instance&lt;/li&gt;
  &lt;li&gt;Use migration-based database delivery. Store your migrations in your version control system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When writing integration tests for the database:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Separate database connections from transactions. Use repositories and transactions.&lt;/li&gt;
  &lt;li&gt;Don’t reuse database transactions or units of work between sections of the test. Integration tests should replicate the production environment as closely as possible. This means the Act part shouldn’t share connections or database context with anyone else.&lt;/li&gt;
  &lt;li&gt;Clean up data at the beginning of each test. Create a base class and put all the deletion scripts there.&lt;/li&gt;
  &lt;li&gt;Don’t use in-memory databases. They don’t have the same set of features. Use the same database system as production.&lt;/li&gt;
  &lt;li&gt;Extract technical, non-business-related parts into helper methods. For Arrange parts, use object mothers. And, for Assert parts, create extension methods for data assertions, like, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userFromDb.ShouldExist()&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Test only the most complex or important read operations. Forget about the rest.&lt;/li&gt;
  &lt;li&gt;Don’t test repositories directly. Test them as part of an overarching integration test suite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voilà! These are my takeaways. Although this book has “Unit Testing” in its title, I really liked it covers integration tests, especially testing the database and data-access layer. I’d say this isn’t a book for beginners. You would take more out of this book if you read &lt;a href=&quot;/2020/03/06/TheArtOfUnitTestingReview/&quot;&gt;The Art Of Unit Testing&lt;/a&gt; first.&lt;/p&gt;

&lt;p&gt;If you want to read more about unit testing, check my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover from what unit testing is to unit testing best practices.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Hands-on Domain-Driven Design with .NET Core: Takeaways</title>
   <link href="https://canro91.github.io/2022/10/03/HandsOnDDDTakeaways/"/>
   <updated>2022-10-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/10/03/HandsOnDDDTakeaways</id>
   <content type="html">&lt;p&gt;If you’re new to Domain-Driven Design, this book is a good starting point. It’s a “hands-on” book. It walks through a sample marketplace for ads. It shows from what Domain-Driven Design is to how to evolve a system. Also, it contains a couple of chapters with a good introduction to Event Sourcing. These are my takeaways.&lt;/p&gt;

&lt;h2 id=&quot;ddd-and-ubiquitous-language&quot;&gt;DDD and Ubiquitous Language&lt;/h2&gt;

&lt;p&gt;The main point of Domain-Driven Design (DDD) is sharing the domain language between domain experts and developers in meetings, documentation, and even in code. That’s what we call a “Ubiquitous Language.”&lt;/p&gt;

&lt;p&gt;In our code, we should make all domain concepts explicit and express intent clearly. For example, in a software system to register Paid time off, what do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StartDate&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EndDate&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HalfDate&lt;/code&gt; mean? Does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StartDate&lt;/code&gt; refer to the last day at work or the first non-working day? What about: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstDayNotAtWork&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CameBackToWork&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LeftDuringWorkday&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;A ubiquitous language makes sense in a context. For example, a product doesn’t mean the same thing for the Sales, Purchasing, and Inventory departments.&lt;/p&gt;

&lt;p&gt;Therefore, we should avoid God-classes like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Product&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Customer&lt;/code&gt; with properties for all possible views of the physical object, since not all properties need to be populated at a given time.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1585849834908-3481231155e8?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2Mzk1MjgxNw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Half of a red onion on a white background&quot; /&gt;

&lt;figcaption&gt;Does your architecture make you cry too? Photo by &lt;a href=&quot;https://unsplash.com/@k8_iv?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;K8&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/onion?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;onion-architecture-and-cqrs&quot;&gt;Onion Architecture and CQRS&lt;/h2&gt;

&lt;p&gt;This book advocate using the Onion Architecture and Command-query Responsibility Segregation (CQRS) when implementing DDD.&lt;/p&gt;

&lt;p&gt;When following the Onion Architecture, the Domain is the center of everything, and everything depends on it. Application services and Infrastructure are layers around this core. Apart from standard libraries and some base classes, the Domain shouldn’t have any references.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“A good rule of thumb here is that the whole domain model should be testable without involving any infrastructure. Primarily, in your domain model tests, you should not use test harnesses and mocks.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CQRS distinguishes between write and read operations using commands that mutate the system and queries that return the system state.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-10-03-HandsOnDDDTakeaways/CQRS.png&quot; alt=&quot;CQRS commands and queries&quot; /&gt;
    &lt;figcaption&gt;CQRS commands and queries flow&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;To implement CQRS, we can use database-mapped domain objects to mutate the system and SQL queries to retrieve the system, ignoring the domain model.&lt;/p&gt;

&lt;h2 id=&quot;ddd-mechanics&quot;&gt;DDD Mechanics&lt;/h2&gt;

&lt;p&gt;To implement a domain model in code, DDD has some recognizable types of objects like entities, value objects, events, and services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entities&lt;/strong&gt; should have an Id, accessible from the outside. IDs are database unique keys or GUIDs. We shouldn’t change an entity by changing its properties from outside the entity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Value objects&lt;/strong&gt; should be immutable. Two entities are the same by their identity but value objects by their value. To validate entity invariants, we can use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnsureValidState()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Events&lt;/strong&gt; are reactions to executions of commands. Events represent data in commands and other details from the changed entity, like the Id. Events should only contain primitive types. With events, we can notify changes in one part of the system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application services&lt;/strong&gt; accept commands and use the Domain to handle the operation. An application service is responsible for translating primitive types to value objects. Often, all application services follow a similar script: they retrieve an entity from the database, mutate it and update the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aggregate Roots&lt;/strong&gt; work like a parent entity that changes its state as a whole. We should only reference, access, and manipulate child objects of an aggregate through the aggregate boundary.&lt;/p&gt;

&lt;h2 id=&quot;queries-repositories-and-databases&quot;&gt;Queries, Repositories, and Databases&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“A domain model exists on its own, and it is designed to deal with business rules and invariants, and not to deal with the database.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There’s a distinction between repositories and queries. Repositories deal with the aggregate state. In a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassifiedAdRepository&lt;/code&gt;, we only should get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassifiedAds&lt;/code&gt;. For all other data access, we should use queries.&lt;/p&gt;

&lt;p&gt;We should write queries using the Ubiquitous Language too. For example, let’s write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetAdsPendingReview()&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetAds(ad =&amp;gt; ad.State == State.PendingReview)&lt;/code&gt;. And we can access the storage directly on our query handlers. That’s fine.&lt;/p&gt;

&lt;p&gt;For example, this is a query to return active classified ads. We can put it inside the API layer directly,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Queries&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PublicClassifiedAdListItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;QueryPublishedClassifiedAds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbConnection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someDbConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;QueryModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPublishedClassifiedAds&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someDbConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QueryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PublicClassifiedAdListItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Plain old SQL query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;State&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClassifiedAdState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;PageSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Offset&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PageSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I really like the simplicity of using queries instead of &lt;a href=&quot;/2023/08/07/TooManyLayers/&quot;&gt;too many artifacts and layers of indirection&lt;/a&gt; to read data.&lt;/p&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting Thoughts&lt;/h2&gt;

&lt;p&gt;Voilà! Those are my takeaways. I’d say it’s a good book to learn about DDD for the first time. There are things I liked and didn’t like about this book.&lt;/p&gt;

&lt;p&gt;I liked that the book contains a practical example, a marketplace for ads, not only theory. If you want to follow along with the code sample, read these chapters: 4, 5, 6, 7, and 9. Skip most of chapter 8 if you already know how to set up EntityFramework. Skim through all others.&lt;/p&gt;

&lt;p&gt;I liked how the sample application doesn’t use interfaces just for the sake of it. I’ve seen so many single-implementation interfaces to only use a dependency container and test it with mocks. And I also liked how query handlers use SQL statements directly instead of using another layer of indirection.&lt;/p&gt;

&lt;p&gt;But, I didn’t like that the sample application ended up with “application services” instead of “command handlers.” I was expecting a command handler per each command and API method. The only sample application service has a huge switch statement to handle every command. Argggg!&lt;/p&gt;

&lt;p&gt;For more takeaways, check &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional: Takeaways&lt;/a&gt;. Don’t miss &lt;a href=&quot;/2020/12/10/PrimitiveObsession/&quot;&gt;A case of Primitive Obsession&lt;/a&gt;, it shows how to put in place classes (or records) to replace primitive types. And, my heuristics to &lt;a href=&quot;/2022/12/21/WhenToChooseValueObjects/&quot;&gt;choose Value Objects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happing coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Time zones and NDC Conference</title>
   <link href="https://canro91.github.io/2022/09/05/MondayLinks/"/>
   <updated>2022-09-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/09/05/MondayLinks</id>
   <content type="html">&lt;p&gt;Last month I followed the NDC Conference on YouTube. In this Monday Links episode, I share some of the conferences I watched and liked. I don’t know why but I watched presentations about failures, aviation disasters, and software mistakes. Well, two of the 5 links aren’t about that. Enjoy!&lt;/p&gt;

&lt;h2 id=&quot;improve-working-across-time-zones&quot;&gt;Improve working across time zones&lt;/h2&gt;

&lt;p&gt;Prefer document-based over meeting-based documentation. Only schedule meetings for discussions and have a clear agenda for everyone to review before the meeting. After the meeting, share the conclusions with people in different time zones who couldn’t join. &lt;a href=&quot;https://askwhy.substack.com/p/improve-working-across-time-zones&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;mayday-software-lessons-from-aviation-disasters&quot;&gt;Mayday! Software lessons from aviation disasters&lt;/h2&gt;

&lt;p&gt;This is a conference from NDC. It shows two case studies from aviation disasters and how they relate to software engineering. For the first case study, after an incident, a security expert asked his team these questions to identify the cause of the incident:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How can I prove myself wrong?&lt;/li&gt;
  &lt;li&gt;What details might I be ignoring because it doesn’t fit my theory or solution?&lt;/li&gt;
  &lt;li&gt;What else could cause this issue or situation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Experts traced the root of the incident ten years before the crash: counterfeit parts. This makes us wonder about counterfeit code: code we copy from StackOverflow, blogs, and documentation. We’re responsible for every line of code we write, even for the ones we copy and paste.&lt;/p&gt;

&lt;p&gt;The second case study teaches us some good lessons about communication.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/_oPzRQExVvM?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;failure-is-always-an-option&quot;&gt;Failure is Always an Option&lt;/h2&gt;

&lt;p&gt;From space accidents to the British Post Office to a Kenya money transfer company, this talk shows how new businesses and branches of Science come out of failures and unanticipated usages of systems. Inspired by and contradicting one line in the Apollo 13 movie, “Failure is not an option.”&lt;/p&gt;

&lt;p&gt;This talk claims that the single point of failure of modern cloud-based solutions is the credit card paying the cloud provider. LOL!&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/Av3QP940L2c?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;hacking-c-development-for-the-truly-lazy&quot;&gt;Hacking C#: Development for the Truly Lazy&lt;/h2&gt;

&lt;p&gt;This talk shows a bag of tricks to make code more readable. It shows how to use C# extension methods to remove duplication. Also, it presents the “Commandments of Extension Methods:”&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No business logic&lt;/li&gt;
  &lt;li&gt;Keep them as small as possible&lt;/li&gt;
  &lt;li&gt;Keep them generic, so you can use them with any object&lt;/li&gt;
  &lt;li&gt;Keep them portable&lt;/li&gt;
  &lt;li&gt;Use them where there is boring and repetitive code&lt;/li&gt;
  &lt;li&gt;Make them useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ah! I learned we can make indexers receive multiple indexes. Like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;something[1, 3, 5]&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/0ial6pfgV9g?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;programmings-greatest-mistakes&quot;&gt;Programming’s Greatest Mistakes&lt;/h2&gt;

&lt;p&gt;I had a coworker that always said: “Nobody is going to die,” when somebody else was reluctant to change some code. It turned out we weren’t working on a medical or aerospatial domain. But often, oops cause businesses to lose money. I bet you have taken down servers because of an unoptimized SQL query. That happened to a friend of a friend of mine. Wink, wink!&lt;/p&gt;

&lt;p&gt;It starts by showing one stupid mistake the author made in his early days using a sarcastic name for one of his support tools. The support team ended up shipping it to their clients. Y2K, a missing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt; in a mission-critical software, null, and other mistakes.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/qC_ioJQpv4E?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Voilà! Do you also follow the NDC Conference? What are your own programming’s greatest mistakes? Don’t be ashamed. All of us have one. Until next Monday Links!&lt;/p&gt;

&lt;p&gt;In the meantime, check my &lt;a href=&quot;https://www.educative.io/courses/getting-started-linq-c-sharp&quot;&gt;Getting Started with LINQ course&lt;/a&gt; where I cover from what LINQ is to its most recent methods and overloads introduced in .NET6. And don’t miss the previous &lt;a href=&quot;/2022/08/01/MondayLinks/&quot;&gt;Monday Links on Storytelling, Leet Code, and Boredom&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Three set-like LINQ methods: Intersect, Union, and Except</title>
   <link href="https://canro91.github.io/2022/08/22/IntersectUnionAndExcept/"/>
   <updated>2022-08-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/08/22/IntersectUnionAndExcept</id>
   <content type="html">&lt;p&gt;So far we have covered some of the &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;most common LINQ methods&lt;/a&gt;. This time let’s cover three LINQ methods that work like set operations: Intersect, Union, and Except.&lt;/p&gt;

&lt;p&gt;Like &lt;a href=&quot;/2022/07/25/LinqAggregateExplained/&quot;&gt;the Aggregate method&lt;/a&gt;, we don’t use these methods every day, but they will come in handy from time to time.&lt;/p&gt;

&lt;h2 id=&quot;1-intersect&quot;&gt;1. Intersect&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Intersect() finds the common elements between two collections.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s find the movies we both have watched and rated in our catalogs.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We have not exactly a tie here...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pulp Fiction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Forrest Gump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We have not exactly a tie here...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weBothHaveSeen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Intersect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;We both have seen:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weBothHaveSeen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We both have seen:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we have two lists of movies, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mine&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yours&lt;/code&gt;, with the ones I’ve watched and the ones you have watched, respectively. Also, we both have watched “My Neighbor Totoro” and “Terminator 2.”&lt;/p&gt;

&lt;p&gt;To find the movies we both have seen (the intersection between our two catalogs), we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intersect()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But, our example only shows “My Neighbor Totoro.” What happened here?&lt;/p&gt;

&lt;p&gt;If we pay close attention, we both have watched “Terminator 2,” but we gave it different ratings. Since we’re using &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;records from C# 9.0&lt;/a&gt;, records have member-wise comparison. Therefore, our two “Terminator 2” instances aren’t exactly the same, even though they have the same name. That’s why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intersect()&lt;/code&gt; doesn’t return it.&lt;/p&gt;

&lt;p&gt;To find the common movies using only the movie name, we can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;pass a custom comparer to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intersect()&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;override the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equals()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode()&lt;/code&gt; methods of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; record, or,&lt;/li&gt;
  &lt;li&gt;use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IntersectBy()&lt;/code&gt; method &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;introduced in .NET6&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IntersectBy()&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weBothHaveSeen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IntersectBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    ^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Your movie names&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//               ^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// keySelector: Property to compare by&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;We both have seen:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weBothHaveSeen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We both have seen:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Terminator 2,My Neighbor Totoro&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Unlike Intersect(), IntersectBy() expects a “keySelector,” a delegate with the property to use as the comparing key, and a second collection with the same type as the keySelector.&lt;/strong&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1569003280089-4f68b6367743?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2MDYwNDQxMA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Colorful apartments in a building&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@martfoto1?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Martin Woortman&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/boxes?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-union&quot;&gt;2. Union&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Union() finds the elements from both collections without duplicates.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s find all the movies we have in our catalogs.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pulp Fiction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Forrest Gump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTheMoviesWeHaveSeen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All the movies we have seen:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allTheMoviesWeHaveSeen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// All the movies we have seen:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Terminator 2,Titanic,The Fifth Element,My Neighbor Totoro,Pulp Fiction,Forrest Gump&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time we gave the same rating to our shared movies: “Terminator 2” and “My Neighbor Totoro.” And, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Union()&lt;/code&gt; showed all the movies from both collections, showing duplicates only once.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Union()&lt;/code&gt; works the same way as the union operation in our Math classes.&lt;/p&gt;

&lt;p&gt;LINQ has a similar method to “combine” two collections into a single one: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Concat()&lt;/code&gt;. But, unlike &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Union()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Concat()&lt;/code&gt; returns all elements from both collections without removing the duplicated ones.&lt;/p&gt;

&lt;p&gt;.NET 6.0 also has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnionBy()&lt;/code&gt; method to “union” two collections with a keySelector. And, unlike &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IntersectBy()&lt;/code&gt;, we don’t need the second collection to have the same type as the keySelector.&lt;/p&gt;

&lt;h2 id=&quot;3-except&quot;&gt;3. Except&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Except() finds the elements in one collection that are not present in another one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This time, let’s find the movies only I have watched.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//         ^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//         ^^^^^^^^^^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pulp Fiction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Forrest Gump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onlyIHaveSeen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Only I have seen:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onlyIHaveSeen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Only I have seen:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Titanic,The Fifth Element&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Except()&lt;/code&gt;, we found the movies in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mine&lt;/code&gt; that are not in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yours&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Except()&lt;/code&gt;, we should pay attention to the order of the collection because this method isn’t commutative. This means, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mine.Except(yours)&lt;/code&gt; is not the same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yours.Except(mine)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Likewise, we have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExceptBy()&lt;/code&gt; that receives a KeySelector and a second collection with the same type as the keySelector type.&lt;/p&gt;

&lt;p&gt;Voilà! These are the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intersect()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Union()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Except()&lt;/code&gt; methods. They work like the Math set operations: intersection, union, and symmetrical difference, respectively. Of the three, I’d say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Except&lt;/code&gt; is the most common method.&lt;/p&gt;

&lt;p&gt;If you want to read more about LINQ, check &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;my quick guide to LINQ&lt;/a&gt;, &lt;a href=&quot;/2022/06/13/LinqMistakes/&quot;&gt;five common LINQ mistakes and how to fix them&lt;/a&gt; and &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;what’s new in LINQ with .NET6&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>NCache &amp; Full-Text Search</title>
   <link href="https://canro91.github.io/2022/08/08/FullTextSearchWithNCache/"/>
   <updated>2022-08-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/08/08/FullTextSearchWithNCache</id>
   <content type="html">&lt;p&gt;I bet you have used the SQL LIKE operator to find a keyword in a text field. For large amounts of text, that would be slow. Let’s learn how to implement a full-text search with Lucene and NCache.&lt;/p&gt;

&lt;h2 id=&quot;what-is-full-text-search&quot;&gt;What is Full-Text Search?&lt;/h2&gt;

&lt;p&gt;Full-text search is a technique to search not only exact matches of a keyword in some text but for patterns of text, synonyms, or close words in large amounts of text.&lt;/p&gt;

&lt;p&gt;To support large amounts of text, searching is divided into two phases: indexing and searching. In the indexing phase, an analyzer processes text to create indexes based on the rules of a spoken language like English to remove stop words and record synonyms and inflections of words. Then, the searching phase only uses the indexes instead of the original text source.&lt;/p&gt;

&lt;h2 id=&quot;full-text-search-with-lucene-and-ncache&quot;&gt;Full-Text Search with Lucene and NCache&lt;/h2&gt;

&lt;h3 id=&quot;1-why-lucene-and-ncache&quot;&gt;1. Why Lucene and NCache?&lt;/h3&gt;

&lt;p&gt;From &lt;a href=&quot;https://lucenenet.apache.org/index.html&quot;&gt;its official page&lt;/a&gt;, “Apache Lucene.NET is a high performance search library for .NET.” It’s a C# port of Java-based Apache Lucene, an “extremely powerful” and fast search library optimized for full-text search.&lt;/p&gt;

&lt;p&gt;NCache gives distributed capabilities to Lucene by implementing the Lucene API on top of its In-Memory Distributed cache. This way, NCache makes Lucene a linearly scalable full-text searching solution for .NET. For more features of Distributed Lucene, check &lt;a href=&quot;https://www.alachisoft.com/ncache/distributed-lucene.html&quot;&gt;NCache Distributed Lucene page&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;2-create-a-lucene-cache-in-ncache&quot;&gt;2. Create a Lucene Cache in NCache&lt;/h3&gt;

&lt;p&gt;We have already &lt;a href=&quot;/2022/04/11/DistributedCacheWithNCache/&quot;&gt;installed and used NCache as a IDistributedCache provider&lt;/a&gt;. This time, let’s use NCache version 5.3 to find movies by title or director name using Lucene’s full-text search.&lt;/p&gt;

&lt;p&gt;Lucene stores data in immutable “segments,” which consist of multiple files. We can store these segments in our local file system or in RAM. But, since we’re using Lucene with NCache, we’re storing these segments in NCache.&lt;/p&gt;

&lt;p&gt;Before indexing and searching anything, first, we need to create a Distributed Lucene Cache. Let’s navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8251&lt;/code&gt; to fire NCache Web Manager and add a New Distributed Cache.&lt;/p&gt;

&lt;p&gt;Let’s select “Distributed Lucene” in the Store Type and give it a name. Then, let’s add our own machine and a second node. For write operations, we need at least two nodes. We can stick to the defaults for the other options.&lt;/p&gt;

&lt;p&gt;By default, in Windows machines, NCache stores Lucene indexes in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\ProgramData\ncache\lucene-index&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-08-08-FullTextSearchWithNCache/1-StoreType.png&quot; alt=&quot;NCache Store Type as Distributed Lucene&quot; /&gt;
    &lt;figcaption&gt;NCache Store Type as Distributed Lucene&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For more details about these installation options, check &lt;a href=&quot;https://www.alachisoft.com/resources/docs/ncache/getting-started-guide-windows/create-lucene-cache.html&quot;&gt;NCache official docs&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1485095329183-d0797cdc5676?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY1ODYxODA1MQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Movie theater&quot; /&gt;

&lt;figcaption&gt;Let&apos;s index some movies, shall we? Photo by &lt;a href=&quot;https://unsplash.com/@jakehills?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Jake Hills&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/film?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;3-index-some-movies&quot;&gt;3. Index Some Movies&lt;/h3&gt;

&lt;p&gt;After creating the Distributed Lucene cache, let’s populate our Lucene indexes with some movies from a Console app. Later, we will search them from another Console app.&lt;/p&gt;

&lt;p&gt;First, let’s create a Console app to load some movies to the Lucene Cache. Also, let’s install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lucene.Net.NCache&lt;/code&gt; NuGet package.&lt;/p&gt;

&lt;p&gt;In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; file, we could load all movies we want to index from a database or another store. For example, let’s use a list of movies from IMDb. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Entities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SearchService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;searchService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SomeMoviesFromImdb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Press any key to continue...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This list of movies was taken from IMDb dump&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// See: https://www.imdb.com/interfaces/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeMoviesFromImdb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Caged Fury&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1983&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3.8f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;89&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Maurizio Angeloni&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1959&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Crime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drama&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bad Posture&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2011&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;93&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Jack Smith&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1932&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Drama&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Romance&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Flying Wife&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;91&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Franz Bi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1899&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Comedy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fantasy&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Modern Love&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1990&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5.2f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;105&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sophie Carlhian&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1962&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Comedy&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Sins&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2012&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2.3f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;84&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pierre Huyghe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1962&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Genre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thriller&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Some other movies here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we used a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SearchService&lt;/code&gt; to handle the index creation in a method called  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadMovies()&lt;/code&gt;. Let’s take a look at it.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Analysis.Standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Entities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SearchService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IndexName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;movies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LuceneVersion&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;luceneVersion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LuceneVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LUCENE_48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SearchService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_cacheName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;LoadMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NCacheDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IndexName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 1. Opening directory    ^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;standardAnalyzer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StandardAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;luceneVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexWriterConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;luceneVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;standardAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;OpenMode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CREATE&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;writer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 2. Creating a writer   ^^^&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapToLuceneDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;writer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     ^^^^^^^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 3. Adding a document&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;writer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//     ^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 4. Writing documents&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A bit of background first, Lucene uses documents as the unit of search and index. Documents can have many fields, and we don’t need a schema to store them.&lt;/p&gt;

&lt;p&gt;We can search documents using any field. Lucene will only return those with that field and matching data. For more details on some Lucene internals, check its &lt;a href=&quot;https://lucenenet.apache.org/quick-start/introduction.html&quot;&gt;Lucene Quick Start guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Notice we started our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadMovies&lt;/code&gt; by opening an NCache directory. We needed the same cache name we configured before and an index name. Then we created an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IndexWriter&lt;/code&gt; with our directory and some configurations, like a Lucene version, an analyzer, and an open mode.&lt;/p&gt;

&lt;p&gt;Then, we looped through our movies and created a Lucene document for each one using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapToLuceneDocument()&lt;/code&gt; extension method. Here it is,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Entities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoviesExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapToLuceneDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;directorName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To create Lucene documents, we used two fields of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TextField&lt;/code&gt;: movie name and director name. For each field, we need a name and a value to index. We will use the field names later to create a response object from search results.&lt;/p&gt;

&lt;p&gt;There are two basic field types for Lucene documents: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TextField&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringField&lt;/code&gt;. The first one has support for Full-Text search and the second one supports searching for exact matches.&lt;/p&gt;

&lt;p&gt;Once we called the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Commit()&lt;/code&gt; method, NCache stored our movies in a distributed index.&lt;/p&gt;

&lt;h3 id=&quot;4-full-text-searching-movies&quot;&gt;4. Full-Text Searching Movies&lt;/h3&gt;

&lt;p&gt;Now that we populated our index with some movies, to search them, let’s create another Console app to read a Lucene query.&lt;/p&gt;

&lt;p&gt;Again, let’s use the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SearchService&lt;/code&gt;, this time with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SearchByNames()&lt;/code&gt; method passing a Lucene query.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Analysis.Standard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.QueryParsers.Classic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Lucene.Net.Util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Entities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Responses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SearchMovies.Shared.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SearchService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Same SearchService as before...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SearchByNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NCacheDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_cacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IndexName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectoryReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                 ^^^^^^^^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 1. Creating a reader&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searcher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexSearcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;analyzer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StandardAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;luceneVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;QueryParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;luceneVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;analyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;searchQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//          ^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 2. Parsing a Lucene query &lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 3. Searching documents&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MovieResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TotalHits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ScoreDocs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapToMovieResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     ^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 4. Populating a result object&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, instead of creating an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IndexWriter&lt;/code&gt;, we used a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DirectoryReader&lt;/code&gt; and a query parser with the same Lucene version and analyzer. Then, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Search()&lt;/code&gt; method with the parsed query and a result count. The next step was to loop through the results and create a response object.&lt;/p&gt;

&lt;p&gt;To create a response object from a Lucene document, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapToMovieResponse()&lt;/code&gt;. Here it is,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MovieResponse&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapToMovieResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Document&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MovieResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;directorName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get()&lt;/code&gt; method with the same field names as before to retrieve fields from documents.&lt;/p&gt;

&lt;p&gt;For example, let’s find all movies whose director’s name contains “ca”, with the query &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directorName:ca*&lt;/code&gt;,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-08-08-FullTextSearchWithNCache/2-DirectorsWithca.png&quot; alt=&quot;Movies with director name contains &apos;ca&apos;&quot; /&gt;
    &lt;figcaption&gt;Movies with director name contains &apos;ca&apos;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Of course, there are more keywords in &lt;a href=&quot;https://www.lucenetutorial.com/lucene-query-syntax.html&quot;&gt;Lucene Query Syntaxt&lt;/a&gt; than the ones we used here.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to use Distributed Lucene with NCache. If we already have an implementation with Lucene.NET, we would need few code changes to migrate it to Lucene with NCache. Also, notice that &lt;a href=&quot;https://www.alachisoft.com/resources/docs/ncache/prog-guide/lucene-ncache.html#not-supported-lucene-api&quot;&gt;NCache doesn’t implement all Lucene methods&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To follow along with the code we wrote in this post, check my &lt;a href=&quot;https://github.com/canro91/NCacheDemo&quot;&gt;Ncache Demo&lt;/a&gt; repository over on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/NCacheDemo&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/NCacheDemo.svg&quot; alt=&quot;canro91/NCacheDemo - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To read more content, check my post &lt;a href=&quot;/2022/04/11/DistributedCacheWithNCache/&quot;&gt;Working with ASP.NET Core IDistributedCache Provider for NCache&lt;/a&gt; to learn about caching with ASP.NET Core and NCache.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this post in collaboration with &lt;a href=&quot;https://www.alachisoft.com/&quot;&gt;Alachisoft&lt;/a&gt;, NCache creators.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Storytelling, Leet Code and Boredom</title>
   <link href="https://canro91.github.io/2022/08/01/MondayLinks/"/>
   <updated>2022-08-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/08/01/MondayLinks</id>
   <content type="html">&lt;p&gt;In case you find the Monday Links series for the first time: these are five links from past weeks that I found interesting (and worth sharing) while &lt;del&gt;procastinating&lt;/del&gt; surfing the Web. This is not a link-building scheme, I only read and liked these articles.&lt;/p&gt;

&lt;h2 id=&quot;the-secret-art-of-storytelling-in-programming-by-yehonathan-sharvit&quot;&gt;The Secret Art of Storytelling in Programming by Yehonathan Sharvit&lt;/h2&gt;

&lt;p&gt;This presentation starts with the author sharing his struggle to read books as a kid. And later read code as a programmer and contracts as a consultant.&lt;/p&gt;

&lt;p&gt;The main message from this presentation is how memory, attention, and structure spans relate to coding. The author presents three coding style principles that respect mind spans:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Use small functions&lt;/li&gt;
  &lt;li&gt;Make every line in a function have the same level of abstraction&lt;/li&gt;
  &lt;li&gt;Use descriptive names instead of comments&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=xbikBoA3Oik&quot;&gt;Watch full presentation&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;stop-interviewing-with-leet-code&quot;&gt;Stop Interviewing With Leet Code&lt;/h2&gt;

&lt;p&gt;Interviewing is broken. We all agree. But we don’t know how to fix it. Brain teasers, IQ tests, pair programming, algorithms? This post presents an alternative: inspect the candidate GitHub and public work, ask him to review some piece of code, add unit tests or do some refactors. That sounds like a better idea! &lt;a href=&quot;https://fev.al/posts/leet-code/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1575997945931-3c57bce69943?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY1NjYwOTQ0NA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;International Harvester, Spare Parts Counter, Sydney, 1947&quot; /&gt;

&lt;figcaption&gt;Was he looking for a job? Photo by &lt;a href=&quot;https://unsplash.com/@museumsvictoria?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Museums Victoria&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;how-to-professionally-say&quot;&gt;How to professionally say…&lt;/h2&gt;

&lt;p&gt;One of the things we learn while working for a company is office politics. Basically, how to say things and to step away from certain situations. I learned from a coworker to say “I don’t have enough information to answer that question” instead of a simple “I don’t know.” You will find more lines like that one in this post. &lt;a href=&quot;https://howtoprofessionallysay.akashrajpurohit.com&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-feel-engaged-at-work-a-software-engineers-guide&quot;&gt;How to feel engaged at work: a software engineer’s guide&lt;/h2&gt;

&lt;p&gt;Let’s be honest. It’s rewarding when we see the impact of our work. But, often, all days look almost the same. Another JIRA ticket for a production issue. Another meeting that could have been an email. This article shows four ideas to spice things up. &lt;a href=&quot;https://jasont.co/ennui/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-toxic-grind&quot;&gt;The Toxic Grind&lt;/h2&gt;

&lt;p&gt;This article talks about success, hard work, and work-life balance. This is my favorite line: &lt;em&gt;“We should glorify the journey of achieving something meaningful, not the dream of wealth and power. Glorify the skills you build along the way, not the shortcuts you take.”&lt;/em&gt; &lt;a href=&quot;https://vadimkravcenko.com/shorts/the-toxic-grind/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links. What do you do to feel engaged at your work? Have you been asked to solve LeetCode questions during interviews? Would you like to do something different in future interviews?&lt;/p&gt;

&lt;p&gt;In the meantime, check my &lt;a href=&quot;https://www.educative.io/courses/getting-started-linq-c-sharp&quot;&gt;Getting Started with LINQ course&lt;/a&gt; where I cover from what LINQ is to its most recent methods and overloads introduced in .NET6. And don’t miss the previous &lt;a href=&quot;/2022/05/23/MondayLinks/&quot;&gt;Monday Links on Staging environments, Work and Types&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>LINQ Aggregate Method Explained with Pictures</title>
   <link href="https://canro91.github.io/2022/07/25/LinqAggregateExplained/"/>
   <updated>2022-07-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/07/25/LinqAggregateExplained</id>
   <content type="html">&lt;p&gt;This is not &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;one of the most used LINQ methods&lt;/a&gt;. We won’t use it every day. But, it’s handy for some scenarios. Let’s learn how to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Aggregate method applies a function on a collection carrying the result to the next element. It “aggregates” the result of a function over a collection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate&lt;/code&gt; method takes two parameters: a seed and an aggregating function that takes the accumulated value and one element from the collection.&lt;/p&gt;

&lt;h2 id=&quot;how-does-aggregate-work&quot;&gt;How does Aggregate work?&lt;/h2&gt;

&lt;p&gt;Let’s reinvent the wheel to understand &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate&lt;/code&gt; by finding the maximum rating in our movie catalog. Of course, LINQ has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Max&lt;/code&gt; method. And, &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;.NET 6 introduced new LINQ methods&lt;/a&gt;, among those: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxBy&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxRating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxSoFar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MaxBetween&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxSoFar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                     ^^^^^^^^^&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Maximum rating on our catalog: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Comparing 0 and 4.5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Comparing 4.5 and 4.6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Comparing 4.6 and 4.7&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Comparing 4.7 and 5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Comparing 5 and 4&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Comparing 5 and 5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Maximum rating on our catalog: 5&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MaxBetween&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxSoFar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Comparing &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxSoFar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; and &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxSoFar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxSoFar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate()&lt;/code&gt; with two parameters: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0f&lt;/code&gt; as the seed and the delegate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(maxSoFar, movie) =&amp;gt; MaxBetween(maxSoFar, movie.Rating)&lt;/code&gt; as the aggregating function. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxSoFar&lt;/code&gt; is the accumulated value from previous iterations, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movie&lt;/code&gt; is the current movie while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate&lt;/code&gt; iterates over our list. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxBetween()&lt;/code&gt; method returns the maximum between two numbers.&lt;/p&gt;

&lt;p&gt;Notice the order of the debugging messages we printed every time we compare two ratings in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxBetween()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;On the first iteration, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate()&lt;/code&gt; method executes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxBetween()&lt;/code&gt; aggregating function using the seed (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0f&lt;/code&gt;) and the first element (“Titanic” with 4.5) as parameters.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-07-25-LinqAggregateExplained/Iteration1.png&quot; alt=&quot;Aggregate first iteration on a list of movies&quot; /&gt;
    &lt;figcaption&gt;Aggregate first iteration&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next, it calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxBetween()&lt;/code&gt; with the previous result (4.5) as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxSoFar&lt;/code&gt; and the next element of the collection (“The Fifth Element” with 4.6f).&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-07-25-LinqAggregateExplained/Iteration2.png&quot; alt=&quot;Aggregate second iteration on a list of movies&quot; /&gt;
    &lt;figcaption&gt;Aggregate second iteration&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In the last iteration, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate()&lt;/code&gt; finds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxSoFar&lt;/code&gt; from all previous iterations and the last element (“My Neighbor Totoro” with 5). And it returns the last value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxSoFar&lt;/code&gt; as a result.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-07-25-LinqAggregateExplained/LastIteration.png&quot; alt=&quot;Aggregate last iteration on a list of movies&quot; /&gt;
    &lt;figcaption&gt;Aggregate last iteration&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In our example, we used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate()&lt;/code&gt; with a seed. But, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate()&lt;/code&gt; has an overload without it, then it uses the first element of the collection as the seed. Also, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate()&lt;/code&gt; has another parameter to transform the result before returning it.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aggregate&lt;/code&gt; method works. Remember, it returns an aggregated value from a collection instead of another collection. This is one of those methods we don’t use often. I’ve used it only a couple of times. One of them was in my parsing library, &lt;a href=&quot;/2019/03/08/ATaleOfAPdfParser/&quot;&gt;Parsinator&lt;/a&gt;, to apply a list of modification functions on the same input object &lt;a href=&quot;https://github.com/canro91/Parsinator/blob/master/Parsinator/Extensions/ISkipExtensions.cs#L8&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to read more about LINQ and its features, check &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;my quick guide to LINQ&lt;/a&gt;, &lt;a href=&quot;/2022/06/13/LinqMistakes/&quot;&gt;five common LINQ mistakes and how to fix them&lt;/a&gt; and &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;what’s new in LINQ with .NET6&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Peeking into LINQ DistinctBy source code</title>
   <link href="https://canro91.github.io/2022/07/11/LinqDistinctBySourceCode/"/>
   <updated>2022-07-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/07/11/LinqDistinctBySourceCode</id>
   <content type="html">&lt;p&gt;“Don’t use libraries you can’t read their source code.”&lt;/p&gt;

&lt;p&gt;That’s a bold statement I found and shared in a past &lt;a href=&quot;/2022/01/17/MondayLinks/&quot;&gt;Monday Links&lt;/a&gt; episode. Inspired by that, let’s see what’s inside the new LINQ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistinctyBy&lt;/code&gt; method.&lt;/p&gt;

&lt;h2 id=&quot;what-distinctby-does&quot;&gt;What DistinctBy does&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DistinctBy returns the objects containing unique values based on one of their properties. It works on collections of complex objects, not just on plain values.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;DistinctBy is one of the &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;new LINQ methods introduced in .NET 6&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how to find unique movies by release year.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Schindler&apos;s List&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1993&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8.9f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Lord of the Rings: The Return of the King&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8.9f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pulp Fiction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8.8f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Forrest Gump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Inception&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Here we use the DistinctBy method with the ReleaseYear property&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distinctByReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DistinctBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                                 ^^^^^^^^^^&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distinctByReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Schindler&apos;s List: [1993]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The Lord of the Rings: The Return of the King: [2003]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Pulp Fiction: [1994]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Inception: [2010]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistinctBy&lt;/code&gt; method on a list of movies. We didn’t use it on a list of released years to then find one movie for each unique release year found.&lt;/p&gt;

&lt;p&gt;Before looking at DistinctBy source code, how would you implement it?&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1607451481819-dc811fca803a?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0MDc5OTg4OA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Puppy looking inside a gift bag&quot; /&gt;

&lt;figcaption&gt;Let&apos;s peek into DistinctBy source code. Photo by &lt;a href=&quot;https://unsplash.com/@freestocks?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;freestocks&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;linq-distinctby-source-code&quot;&gt;LINQ DistinctBy source code&lt;/h2&gt;

&lt;p&gt;This is the source code for the DistinctBy method. &lt;a href=&quot;https://github.com/dotnet/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/Distinct.cs#L48&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-07-11-LinqDistinctBySourceCode/DistinctByCode.png&quot; alt=&quot;DistinctBy source code&quot; /&gt;
    &lt;figcaption&gt;DistinctBy source code&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Well, it doesn’t look that complicated. Let’s go through it.&lt;/p&gt;

&lt;h3 id=&quot;1-iterating-over-the-input-collection&quot;&gt;1. Iterating over the input collection&lt;/h3&gt;

&lt;p&gt;First, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistinctBy()&lt;/code&gt; starts by checking its parameters and calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistinctByIterator()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a common pattern in other LINQ methods: Checking parameters in one method and then calling a child iterator method to do the actual logic. (See 1. in the image above)&lt;/p&gt;

&lt;p&gt;Then, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistinctByIterator()&lt;/code&gt; initializes the underling enumerator of the input collection with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt; declaration. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&lt;/code&gt; type has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetEnumerator()&lt;/code&gt; method. (See 2.)&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerator&lt;/code&gt; type has:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoveNext()&lt;/code&gt; method to advance the enumerator to the next position&lt;/li&gt;
  &lt;li&gt;a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current&lt;/code&gt; property to hold the element at the current position.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a collection is empty or if the iterator reaches the end of the collection, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoveNext()&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;. And, when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoveNext()&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Current&lt;/code&gt; gets updated with the element at that position. &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.ienumerator?view=net-6.0&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, to start reading the input collection, the iterator is placed at the initial position of the collection calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoveNext()&lt;/code&gt;. (See 3.) This first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; avoids allocating memory by creating a set in the next step if the collection is empty.&lt;/p&gt;

&lt;h3 id=&quot;2-keeping-only-unique-values&quot;&gt;2. Keeping only unique values&lt;/h3&gt;

&lt;p&gt;After that, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistinctByIterator()&lt;/code&gt; creates a set with a default capacity and an optional comparer. This set keeps track of the unique keys already found. (See 4.)&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-07-11-LinqDistinctBySourceCode/DefaultCapacity.png&quot; alt=&quot;DefaultInternalSetCapacity declaration&quot; /&gt;
    &lt;figcaption&gt;DefaultInternalSetCapacity = 7&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The next step is to read the current element and add its key to the set. (See 5.)&lt;/p&gt;

&lt;p&gt;If a set doesn’t already contain the same element, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add()&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; and adds it to the set. Otherwise, it returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;. And, when the set exceeds its capacity, the set gets resized. &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.add?view=net-6.0#System_Collections_Generic_HashSet_1_Add__0_&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the current element’s key was added to the set, the element is returned with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield return&lt;/code&gt; keywords. This way, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistinctByIterator()&lt;/code&gt; returns one element at a time.&lt;/p&gt;

&lt;p&gt;Step 5 is wrapped inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do-while&lt;/code&gt; loop. It runs until the enumerator reaches the end of the collection. (See 6.)&lt;/p&gt;

&lt;p&gt;Voilà! That’s the DistinctBy source code. Simple but effective. Not that intimidating, after all. The trick was to use a set.&lt;/p&gt;

&lt;p&gt;To learn about LINQ, check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;quick guide to LINQ&lt;/a&gt;, &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;five common LINQ methods in pictures&lt;/a&gt; and &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;what’s new in LINQ with .NET 6.0&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Four new LINQ methods in .NET 6: Chunk, DistinctBy, Take, XOrDefault</title>
   <link href="https://canro91.github.io/2022/06/27/NET6LinqMethods/"/>
   <updated>2022-06-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/06/27/NET6LinqMethods</id>
   <content type="html">&lt;p&gt;LINQ isn’t a new feature in the C# language. It was released back in C# version 3.0 in the early 2000s.&lt;/p&gt;

&lt;p&gt;And, after more than ten years, it was finally updated with the .NET 6 release. These are four of the new LINQ methods and overloads in .NET 6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.NET 6 introduced new LINQ methods like Chunk, DistinctBy, MinBy, and MaxBy. Also, new overloads to existing methods like Take and FirstOrDefault. Before this update, to use similar features, custom LINQ methods or third-party libraries were needed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;1-chunk&quot;&gt;1. Chunk&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Chunk splits a collection into buckets or “chunks” of at most the same size. It receives the chunk size and returns a collection of arrays.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s say we want to watch all movies we have in our catalog. But, we can only watch three films on a single weekend. Let’s use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Chunk&lt;/code&gt; method for that.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Split the movies list into chunks of three movies&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunksOf3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunksOf3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The Fifth Element,Terminator 2,Avatar&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We used three &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;recent C# features&lt;/a&gt;: the Top-level statements, records from C# 9.0, and global using statements from C# 10.0. We don’t need all the boilerplate code to write a Console application anymore.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Chunk&lt;/code&gt; returns “chunks” with at most the given size. It returns fewer elements in the last bucket when there aren’t enough elements in the source collection.&lt;/p&gt;

&lt;h2 id=&quot;2-distinctby&quot;&gt;2. DistinctBy&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Unlike Distinct, DistinctBy receives a delegate to select the property to use as the comparison key and returns the objects containing the distinct values, not only the distinct values themselves.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s find the movies containing unique ratings, not just the ratings.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distinctRatings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DistinctBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;distinctRatings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Titanic,The Fifth Element,Terminator 2,Avatar,Platoon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, there are similar alternatives to existing methods such as MinBy, MaxBy, &lt;a href=&quot;/2022/08/22/IntersectUnionAndExcept/&quot;&gt;ExceptBy, IntersectBy, and UnionBy&lt;/a&gt;. They work with a delegate to select a property to use as the comparison key and return the “containing” objects, not only the result.&lt;/p&gt;

&lt;p&gt;If we &lt;a href=&quot;/2022/07/11/LinqDistinctBySourceCode/&quot;&gt;take a look at DistinctBy source code&lt;/a&gt; to see what’s inside a LINQ method, it’s not that intimidating after all.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1526007413281-c202e21eedf3?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0MDc5ODA3MQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Vintage movie camera&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@jhanks787?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Joshua Hanks&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/cinema?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-take-with-ranges&quot;&gt;3. Take with Ranges&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Take receives a range of indexes to pick a slice of the input collection, not only the first consecutive elements. Take with a range replaces Take followed by Skip to choose a slice of elements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s choose a slice of our catalog using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Take&lt;/code&gt; with &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;ranges and the index-from-end operator&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rangeOfMovies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(^&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rangeOfMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The Fifth Element,Terminator 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Take(^5..3)&lt;/code&gt; selects elements starting from the fifth position from the end (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^5&lt;/code&gt;) up to the third position from the start (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;). We didn’t need to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Skip&lt;/code&gt; method for that.&lt;/p&gt;

&lt;p&gt;Now that we have Take with ranges is easier to find the last “n” elements of a collection: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Take(^n...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s find the last three movies on our catalog.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastThreeMovies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(^&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastThreeMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Avatar,Platoon,My Neighbor Totoro&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before this .NET update, we had to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Skip(movies.Count - 3).Take(3)&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Take&lt;/code&gt; did all the work.&lt;/p&gt;

&lt;h2 id=&quot;4-xordefault-methods-with-an-optional-default-value&quot;&gt;4. XOrDefault methods with an optional default value&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;FirstOrDefault has a new overload to return a default value when the collection is empty or doesn’t have any elements that satisfy the given condition. Other methods with the suffix ‘OrDefault’ have similar overloads.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s find in our catalog of movies a “perfect” film. Otherwise, let’s return our favorite movies from all times.&lt;/p&gt;

&lt;p&gt;Before this update, we had to check if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; returned a non-null value or we had to use the &lt;a href=&quot;/2020/11/17/DefaultOrEmpty/&quot;&gt;LINQ DefaultIfEmpty method&lt;/a&gt;. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTimesFavorite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Back to the Future&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1985&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Using the Null-coalescing assignment ??= operator&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;favorite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTimesFavorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//       ^^^&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Or&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Using the DefaultIfEmpty method&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefaultIfEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allTimesFavorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                     &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                     &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the new overload, we pass a safe default. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTimesFavorite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Back to the Future&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1985&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// We have a safe default now. See the second parameter&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;allTimesFavorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;--&lt;/span&gt;
    
&lt;span class=&quot;c1&quot;&gt;// We don&apos;t need to check for null here&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;favorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Back to the future&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice the second parameter we passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;To use these new methods and overloads, install the latest version of the .NET SDK from the &lt;a href=&quot;https://dotnet.microsoft.com/en-us/download&quot;&gt;.NET official page&lt;/a&gt; and use as target framework at least &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.net6&lt;/code&gt; in your project files.&lt;/p&gt;

&lt;p&gt;Here’s a sample csproj file for a Console app using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.net6&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net6.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--             ^^^^^  --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! These are four new LINQ methods released in the .NET 6 updated. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; with a safe default helps us prevent one of the &lt;a href=&quot;/2022/06/13/LinqMistakes/&quot;&gt;common LINQ mistakes&lt;/a&gt;: using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault&lt;/code&gt; methods without null checking afterwards.&lt;/p&gt;

&lt;p&gt;To learn about LINQ and other methods, check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;quick guide to LINQ&lt;/a&gt;, &lt;a href=&quot;/2022/05/30/HowToUseLinqGroupBy/&quot;&gt;how to use LINQ GroupBy method&lt;/a&gt; and &lt;a href=&quot;/2024/04/15/NET9LinqMethods/&quot;&gt;two new LINQ methods in .NET 9&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Five common LINQ mistakes and how to fix them</title>
   <link href="https://canro91.github.io/2022/06/13/LinqMistakes/"/>
   <updated>2022-06-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/06/13/LinqMistakes</id>
   <content type="html">&lt;p&gt;It’s easy to start working with LINQ to replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt;, and other loops. With &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;a handful of LINQ methods&lt;/a&gt;, we have our backs covered.&lt;/p&gt;

&lt;p&gt;But, often, we make some common mistakes when working with LINQ. Here are five common mistakes we make when working with LINQ for the first time and how to fix them.&lt;/p&gt;

&lt;h2 id=&quot;mistake-1-use-count-instead-of-any&quot;&gt;Mistake 1: Use Count instead of Any&lt;/h2&gt;

&lt;p&gt;We should always prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Count&lt;/code&gt; to check if a collection is empty or has at least one element that meets a condition.&lt;/p&gt;

&lt;p&gt;Let’s write,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt; returns when it finds at least one element.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Count&lt;/code&gt; could use the size of the underlying collection. But, it could also evaluate the entire LINQ query for other collection types. And this could be a performance hit for large collections.&lt;/p&gt;

&lt;h2 id=&quot;mistake-2-use-where-followed-by-any&quot;&gt;Mistake 2: Use Where followed by Any&lt;/h2&gt;

&lt;p&gt;We can use a condition with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt; directly, instead of filtering first with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; to then use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s write,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The same applies to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; method followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Count&lt;/code&gt;, or any other method that receives a filter condition.&lt;/p&gt;

&lt;p&gt;Let’s use the filtering condition directly instead of relying on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; method first.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1613963969191-2a77db9811d2?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY1MTkzODI4MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Spilled coffee on a street&quot; /&gt;

&lt;figcaption&gt;Ooops...Photo by &lt;a href=&quot;https://unsplash.com/@jontyson?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Jon Tyson&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;mistake-3-use-firstordefault-instead-of-singleordefault-to-find-unique-values&quot;&gt;Mistake 3: Use FirstOrDefault instead of SingleOrDefault to find unique values&lt;/h2&gt;

&lt;p&gt;We should prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; to find one and only one element matching a condition inside a collection.&lt;/p&gt;

&lt;p&gt;Let’s write,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We have a tie here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// SigleOrDefault expects only one element...but there are two of them&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theBest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SingleOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                   ^^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// System.InvalidOperationException: &apos;Sequence contains more than one matching element&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theBest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theBest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//        ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We have a tie here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// FirstOrDefault remains quiet if there&apos;s more than one matching element...&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theBest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theBest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theBest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SigleOrDefault&lt;/code&gt; throws an exception when it finds more than one element matching a condition. But, with multiple matching elements, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; returns the first of them without signaling any problem.&lt;/p&gt;

&lt;p&gt;Let’s pick between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault&lt;/code&gt; to show the query’s intent. Let’s prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault&lt;/code&gt; to retrieve a unique matching element from a collection.&lt;/p&gt;

&lt;p&gt;To guarantee that there is a single element in a collection, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Single&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault&lt;/code&gt; have to evaluate the LINQ query over the entire collection. And, again, this could be a performance hit for large collections.&lt;/p&gt;

&lt;h2 id=&quot;mistake-4-use-firstordefault-without-null-checking&quot;&gt;Mistake 4: Use FirstOrDefault without null checking&lt;/h2&gt;

&lt;p&gt;Let’s always check if we have a result when working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LastOrDefault&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When any of those three methods don’t find results, they return the default value of the collection type.&lt;/p&gt;

&lt;p&gt;For objects, the default value would be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; reference. And, do you know what happens when we access a property or method on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; reference?… Yes, It throws &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;the fearsome NullReferenceException&lt;/a&gt;. Arrggg!&lt;/p&gt;

&lt;p&gt;We have this mistake in the following code sample. We forgot to check if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worst&lt;/code&gt; variable has a value. An &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (worst != null)&lt;/code&gt; would solve the problem.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// We forgot to check for nulls after using FirstOrDefault&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// It will break	&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;worst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;worst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                  ^^^^^^^^^^^^ &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// System.NullReferenceException: &apos;Object reference not set to an instance of an object.&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// worst was null.&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wrote a LINQ query with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; looking for the first movie with a rating lower than 2. But, we don’t have any movie that matches that condition. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;, and we forgot to check if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worst&lt;/code&gt; variable was different from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; before using it.&lt;/p&gt;

&lt;p&gt;There are other alternatives to get rid of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt; when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/03/06/NullableOperatorsAndReferences/&quot;&gt;C# 8.0 nullable references&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/11/17/DefaultOrEmpty/&quot;&gt;LINQ DefaultIfEmpty method&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;.NET6 FirstOrDefault with a default value&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;mistake-5-expect-linq-queries-to-be-cached&quot;&gt;Mistake 5: Expect LINQ queries to be cached&lt;/h2&gt;

&lt;p&gt;LINQ queries are lazy-evaluated.&lt;/p&gt;

&lt;p&gt;It means the actual result of a LINQ query is evaluated when we loop through the result, not when we declare the query. And it’s evaluated every time we loop through it.&lt;/p&gt;

&lt;p&gt;Let’s avoid looping through the result of a LINQ query multiple times expecting it to be cached the first time we run it.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Let&apos;s put a debugging message here...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Beep, boop...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 1. Let&apos;s print our favorite movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 2. Let&apos;s do something else with our favorite movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Doing something else with &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro: [5]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Doing something else with My Neighbor Totoro&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wrote a debugging statement inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; method, and we looped through the result twice. The output shows the debugging statements twice. One for every time we looped through the result.&lt;/p&gt;

&lt;p&gt;There was no caching whatsoever. The LINQ query was evaluated every time.&lt;/p&gt;

&lt;p&gt;Instead of expecting a LINQ query to be cached, we could use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToList&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToArray&lt;/code&gt; to break the lazy evaluation. This way, we force the LINQ query to be evaluated only once - when we declare it.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Let&apos;s put a debugging message here...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Beep, boop...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We break the lazy evaluation with ToList&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 1. Let&apos;s print our favorite movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 2. Let&apos;s do something else with our favorite movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Doing something else with &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Output&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Beep, boop...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro: [5]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Doing something else with My Neighbor Totoro&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice the output only shows the debugging messages once, even though we looped through the collection twice. We forced the query to be evaluated only once with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToList&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Voilà! Those are the five most common LINQ mistakes. They seem silly, but we often overlook them. Especially we often forget about the lazy evaluation of LINQ queries.&lt;/p&gt;

&lt;p&gt;For more content about LINQ, check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;Quick Guide to LINQ&lt;/a&gt;, these &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;five common LINQ methods in Pictures&lt;/a&gt;, and &lt;a href=&quot;/2022/05/30/HowToUseLinqGroupBy/&quot;&gt;how to use LINQ GroupBy method&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to use LINQ GroupBy method: Two more use cases</title>
   <link href="https://canro91.github.io/2022/05/30/HowToUseLinqGroupBy/"/>
   <updated>2022-05-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/05/30/HowToUseLinqGroupBy</id>
   <content type="html">&lt;p&gt;Last time, I showed &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;five of the most common LINQ methods with pictures&lt;/a&gt;. Let’s take a deeper look at one of them: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The GroupBy method groups the elements of a collection based on a grouping key. This method returns a collection of “groups” or “buckets” organized by that key.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s continue to work with our catalog of movies and group our movies by rating.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Group our catalog of movies based on their rating&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupedByRating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GroupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                           ^^^^^^^&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupedByRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Each group or bucket has a Key property  &lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Rating: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Rating: 4.5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Titanic&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Rating: 4.6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//The Fifth Element&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Rating: 4.7&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Terminator 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Rating: 5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Avatar&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//My Neighbor Totoro&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Rating: 4&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Platoon&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We used three &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;recent C# features&lt;/a&gt;: the Top-level statements, records, and global using statements. Now we can write Console applications without the Main declaration. All boilerplate code is gone!&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt; method &lt;a href=&quot;/2019/03/22/WhatTheFuncAction/&quot;&gt;receives a delegate as a parameter&lt;/a&gt; with the property to use as a key when grouping elements. In our previous example, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rating&lt;/code&gt; property and wrote &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movie =&amp;gt; movie.Rating&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;what-does-groupby-return&quot;&gt;What does GroupBy return?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The result of GroupBy is a collection of groups or buckets. It returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;IGrouping&amp;lt;TKey, TSource&amp;gt;&amp;gt;&lt;/code&gt; where TKey is the type of the grouping key and TSource is the type of the elements inside the collection.&lt;/strong&gt;&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-05-30-HowToUseLinqGroupBy/GroupByReturn.png&quot; alt=&quot;LINQ GroupBy method signature&quot; /&gt;
    &lt;figcaption&gt;GroupBy return type&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IGrouping&lt;/code&gt; interface is a wrapper for a collection and its grouping key. Each group or bucket has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Key&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;In our example, the type of the return collection was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&amp;lt;IGrouping&amp;lt;float, Movie&amp;gt;&amp;gt;&lt;/code&gt;. That’s why we needed two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; loops to print the movies in each group. One to print the ratings and another to print all movies in each rating.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1515165616480-efd71925068f?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0MzA2MDU3Nw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Magazine stand&quot; /&gt;

&lt;figcaption&gt;We&apos;re grouping films, not magazines. Photo by &lt;a href=&quot;https://unsplash.com/@charissek?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Charisse Kenion&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/pile?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;how-to-transform-every-group&quot;&gt;How to transform every group&lt;/h2&gt;

&lt;p&gt;Also, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt; method transforms each group or bucket.&lt;/p&gt;

&lt;p&gt;Let’s count the movies with the same rating this time.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This is a Console app without the Main class declaration&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// and with Global using statements&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Transform every group into a RatingCount type&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;countByRating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GroupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                                  vvvvvvv&lt;/span&gt;
                                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupedMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RatingCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupedMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;countByRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//4.5: [1]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//4.6: [1]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//4.7: [1]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//5: [2]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//4: [1]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RatingCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We passed a second parameter to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt; method. The first parameter was still the grouping key. But, the second one was a delegate that received the grouping key and the elements of each group,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Func&amp;lt;TKey, IEnumerable&amp;lt;TSource&amp;gt;, TResult&amp;gt;&lt;/code&gt;. We named the two parameters: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rating&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groupedMovies&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we wanted to count the movies with the same rating, we used another LINQ method: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Count&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As its name implies, the Count method returns the number of elements in a collection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With the ratings and the movies per rating, we transformed every group of movies into a new object, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RatingCount&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-to-group-by-more-than-one-property&quot;&gt;How to group by more than one property&lt;/h2&gt;

&lt;p&gt;In the two previous examples, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rating&lt;/code&gt; property as the grouping key. But we can group the elements of a collection by more than one grouping property.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With the GroupBy method, to group a collection by more than one property, use a custom object as the grouping key.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s group our movies by release year and rating.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Life Is Beautiful&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Saving Private Ryan&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Léon: The Professional&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Forrest Gump&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Group by our catalog of moves by release year and rating&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupByReleasedYearAndRating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GroupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                                        ^^^^^^^&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupByReleasedYearAndRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupingKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Release Year/Rating: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groupingKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groupingKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Release Year/Rating: 1998 - 4.5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Titanic&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Saving Private Ryan&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Release Year/Rating: 1997 - 4.6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//The Fifth Element&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Life Is Beautiful&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Release Year/Rating: 1991 - 4.7&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Terminator 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Release Year/Rating: 2009 - 5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Avatar&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Release Year/Rating: 1986 - 4&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Platoon&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Release Year/Rating: 1988 - 5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//My Neighbor Totoro&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Release Year/Rating: 1994 - 4.5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Léon: The Professional&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//Forrest Gump&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We used an anonymous object as the grouping key.&lt;/p&gt;

&lt;h3 id=&quot;what-are-anonymous-objects-in-c&quot;&gt;What are anonymous objects in C#?&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;An anonymous object is a temporary and immutable object defined without a class definition or a name.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In our example, we wrote an anonymous object like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Inside anonymous objects, we can omit names while defining member properties if we want to keep the same names.&lt;/p&gt;

&lt;p&gt;That’s why we only wrote,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we need to access properties from an anonymous object, we use their property names, as usual.&lt;/p&gt;

&lt;p&gt;Voilà! That’s the GroupBy method. It creates groups or buckets with the elements of a collection and transforms each group.&lt;/p&gt;

&lt;p&gt;If you noticed the output of our previous examples, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt; method grouped the elements without sorting them. For that, we would need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderBy&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;To learn about LINQ and other methods, check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;quick guide to LINQ&lt;/a&gt; and &lt;a href=&quot;/2022/08/22/IntersectUnionAndExcept/&quot;&gt;three set-like LINQ methods: Intersect, Union, and Except&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Staging, Work, and Types</title>
   <link href="https://canro91.github.io/2022/05/23/MondayLinks/"/>
   <updated>2022-05-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/05/23/MondayLinks</id>
   <content type="html">&lt;h2 id=&quot;a-career-ending-mistake&quot;&gt;A career-ending mistake&lt;/h2&gt;

&lt;p&gt;Jumping from place to place until we retire? Hopefully, with good pay raises? Being in a team closing Jira tickets and issues until we get bored? Based on the article, the biggest mistake is &lt;em&gt;“not planning the end of our careers.”&lt;/em&gt; The right time to decide the end of our careers is now. The article shows three career alternatives: independent, senior individual contributor (IC), and management. &lt;a href=&quot;https://bitfieldconsulting.com/golang/career&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-we-dont-use-a-staging-environment&quot;&gt;Why we don’t use a staging environment&lt;/h2&gt;

&lt;p&gt;I have lived in almost all the situations described in this post. I worked for a company where we waited months to merge from our staging branch to the production branch. Merge conflicts were a nightmare! Patches going directly to the production environment made things more complicated. Often people forgot to merge patches back to the other environments. Arrrggg!&lt;/p&gt;

&lt;p&gt;Did I mention that we had multiple staging environments? I remember our most-senior team member advocating for ideas like the ones in this post: Getting rid of the “just-in-case” staging environment, merging and publishing everything to production, and using feature flags.&lt;/p&gt;

&lt;p&gt;Oh, I forgot to mention the “do-not-merge-or-touch-staging” times when the Sales team was demoing the product in the same environment. The whole development team had to wait for hours…If we only had had only one environment: production…&lt;a href=&quot;https://squeaky.ai/blog/development/why-we-dont-use-a-staging-environment&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1577895048405-4a186fea845b?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY1MDkyNTQ2NQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Someone working in a store circa 1954&quot; /&gt;

&lt;figcaption&gt;He decided to go independent...Photo by &lt;a href=&quot;https://unsplash.com/@museumsvictoria?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Museums Victoria&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/store?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;algebraic-data-types-in-haskell&quot;&gt;Algebraic Data Types in Haskell&lt;/h2&gt;

&lt;p&gt;This article might seem intimidating at first because of the Haskell syntax. But, it’s a good introduction to Sum and Product types. Custom types are useful when designing business-related entities in our domain. That’s precisely the main premise of &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional&lt;/a&gt;: encode business rules, restrictions, and errors using the type system. &lt;a href=&quot;https://serokell.io/blog/algebraic-data-types-in-haskell&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;maybe-you-should-do-less-work&quot;&gt;Maybe you should do less “work”&lt;/h2&gt;

&lt;p&gt;It contains good points about learning new things at work and making the most value of our working hours. Being efficient, developing other skills, and growing your network. &lt;a href=&quot;https://www.johnwhiles.com/posts/work.html&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-quiet-your-mind-chatter&quot;&gt;How to Quiet Your Mind Chatter&lt;/h2&gt;

&lt;p&gt;You just finished a Zoom call with one of your clients or your boss. But, you kept the conversation going in your head role-playing what you could have said differently.&lt;/p&gt;

&lt;p&gt;We all have experienced that inner voice to imagine different endings to our conversations. Quoting the artcile, &lt;em&gt;“what chatter does is take a stressful experience and prolong it… What makes stress bad is when it’s prolonged over time, and that’s what chatter really does.”&lt;/em&gt; The article shows two strategies to deal with chatter. &lt;a href=&quot;https://nautil.us/how-to-quiet-your-mind-chatter-9641/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Those are this month Monday Links. Do you have a career plan? How many environment do you have between developers’ machines and Production? Three? Do you use your work time to sharpen your skills?&lt;/p&gt;

&lt;p&gt;In the meantime, check my &lt;a href=&quot;https://www.educative.io/courses/getting-started-linq-c-sharp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Getting Started with LINQ course&lt;/a&gt; where I cover from what LINQ is to its most recent methods and overlaod in .NET6. Don’t miss the previous &lt;a href=&quot;/2022/04/25/MondayLinks/&quot;&gt;Monday Links on Blog, Error Messages and Recruiters&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Five LINQ Methods in Pictures</title>
   <link href="https://canro91.github.io/2022/05/16/LINQMethodsInPictures/"/>
   <updated>2022-05-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/05/16/LINQMethodsInPictures</id>
   <content type="html">&lt;p&gt;If you’re learning LINQ for the first time, it can be daunting to learn all LINQ methods at once. Don’t try it.&lt;/p&gt;

&lt;p&gt;Here are the five most common LINQ methods in pictures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LINQ is the declarative, immutable, and lazy-evaluated way of working with collections in C#. And the most frequently used LINQ methods are Where, Select, Any, GroupBy, and FirstOrDefault.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s work with a list of our favorite movies. Let’s write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; class with a name, release year, and a rating.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;1-where&quot;&gt;1. Where&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Where returns a new collection with only the elements that meet a given condition.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; works like a filter on collections. Think of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt; as a replacement for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; in it.&lt;/p&gt;

&lt;p&gt;Let’s filter our list of movies to keep only those with a rating greater than or equal to 4.5.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This query would be something like this,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-05-16-LINQMethodsInPictures/Where.png&quot; alt=&quot;Favorite films filtered by rating&quot; /&gt;
    &lt;figcaption&gt;Let&apos;s keep the films with a rating greater than 4.5&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;We’re using arrows to display our LINQ queries. But, the output of &lt;a href=&quot;/2022/06/13/LinqMistakes/&quot;&gt;a LINQ query is lazy-evaluated&lt;/a&gt;. It means the actual result of a LINQ query is evaluated until we loop through its result.&lt;/p&gt;

&lt;h2 id=&quot;2-select&quot;&gt;2. Select&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Select applies a function to transform every element of a collection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s find only the names of our favorite movies.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;namesOfFavorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This query would be,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-05-16-LINQMethodsInPictures/WhereAndSelect.png&quot; alt=&quot;Name of our favorite films filtered by rating&quot; /&gt;
    &lt;figcaption&gt;Let&apos;s keep only the names of our favorite films&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1542204165-65bf26472b9b?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzOTI1ODI5OA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;8mm filmrolls&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@dmjdenise?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Denise Jans&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/film?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-any&quot;&gt;3. Any&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Any checks if a collection has at least one element matching a condition. Unlike Where and Select, Any doesn’t return a new collection, but either true or false.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s see if we have watched movies with a low rating.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasBadMovies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This query would be,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-05-16-LINQMethodsInPictures/Any.png&quot; alt=&quot;At least one film with a low rating&quot; /&gt;
    &lt;figcaption&gt;Do we have films with a low rating?&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;4-groupby&quot;&gt;4. GroupBy&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GroupBy returns a collection of “buckets” organized by a key. Also, GroupBy transforms each bucket of elements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s count the films with the same rating.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupedByRating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GroupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moviesWithSameRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                &lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moviesWithSameRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The second parameter of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Func&lt;/code&gt; with the grouping key and the elements of each group as parameters. This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Func&lt;/code&gt; works like a mapping function to transform each group or bucket found.&lt;/p&gt;

&lt;p&gt;This query would be,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-05-16-LINQMethodsInPictures/GroupByGroups.png&quot; alt=&quot;Count of films grouped by rating&quot; /&gt;
    &lt;figcaption&gt;Let&apos;s count the films with the same rating&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;/2022/05/30/HowToUseLinqGroupBy/&quot;&gt;GroupBy has other use-cases&lt;/a&gt;, like grouping by more than one property.&lt;/p&gt;

&lt;h2 id=&quot;5-first--firstordefault&quot;&gt;5. First &amp;amp; FirstOrDefault&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;First and FirstOrDefault return the first element in a collection or the first one matching a condition. Otherwise, First throws an exception and FirstOrDefault returns the default value of the collection type.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s find the oldest film we have watched.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This query would be,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-05-16-LINQMethodsInPictures/First.png&quot; alt=&quot;Oldest film we have watched&quot; /&gt;
    &lt;figcaption&gt;Let&apos;s find the oldest film we have watched&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Voilà! Those are five LINQ methods we use more often: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Of course, LINQ has more methods like &lt;a href=&quot;/2022/07/25/LinqAggregateExplained/&quot;&gt;Aggreate&lt;/a&gt;, &lt;a href=&quot;/2022/08/22/IntersectUnionAndExcept/&quot;&gt;Intersect, Union, and Except&lt;/a&gt;, and &lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;new overloads from .NET6&lt;/a&gt;. But, you will get your back covered with those five methods.&lt;/p&gt;

&lt;p&gt;To learn about LINQ and other methods, check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;quick guide to LINQ with examples&lt;/a&gt;. All you need to know to start working with LINQ, in 15 minutes or less.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy LINQ time!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Brent Ozar’s Mastering Courses: My Honest Review</title>
   <link href="https://canro91.github.io/2022/05/02/BrentOzarMasteringCoursesReview/"/>
   <updated>2022-05-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/05/02/BrentOzarMasteringCoursesReview</id>
   <content type="html">&lt;p&gt;This is an honest review of Brent Ozar’s Mastering courses after finishing them all some months ago.&lt;/p&gt;

&lt;p&gt;I couldn’t write this a couple of years ago. Working with databases was a subject I avoided at all costs. Even to the point where I traded database-related tasks with an ex-coworker at a past job.&lt;/p&gt;

&lt;p&gt;Avoiding database concepts cost me painful lessons.&lt;/p&gt;

&lt;p&gt;Like the day I wrote a query with &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;a function around a column in the WHERE&lt;/a&gt; and it almost took the server to its knees. That query was poorly written, and the table didn’t have good indexes. My query ended up making SQL Server scan the whole table. Arrgggg!&lt;/p&gt;

&lt;p&gt;But, it changed a couple of years later while working with one of my clients.&lt;/p&gt;

&lt;p&gt;They asked me to investigate the performance of some critical parts of the app. The bottleneck was in the database and I ended up Googling what to do to speed up SQL Server queries and compiling &lt;a href=&quot;/2020/09/28/SQLServerTuningTips/&quot;&gt;six SQL Server performance tuning tips&lt;/a&gt; I found.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1509541206217-cde45c41aa6d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0Nzg4MDExOQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Airplane cockpit&quot; /&gt;

&lt;figcaption&gt;Don&apos;t press random buttons to make SQL Server faster. Photo by &lt;a href=&quot;https://unsplash.com/@franzharvin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Franz Harvin Aceituna&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/air-plane-cockpit?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In that search for performance tuning advice, I found &lt;a href=&quot;https://training.brentozar.com/?affcode=920087_fhe3khrq&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Brent Ozar and his Mastering courses&lt;/a&gt;. These are the three things I liked after taking them.&lt;/p&gt;

&lt;h2 id=&quot;1-realistic-labs-and-workloads&quot;&gt;1. Realistic Labs and Workloads&lt;/h2&gt;

&lt;p&gt;As part of Brent’s courses, we work with a copy of the StackOverflow database. Yeap, the same StackOverflow we all know and use.&lt;/p&gt;

&lt;p&gt;After every subject in each course, we have labs to finish. Labs with bad queries, no indexes, blocking issues, etc. For the last course, Mastering Server Tuning, we have an emergency to fix. A server is on fire, and we have to put down the fire and lay out a long-term fix.&lt;/p&gt;

&lt;p&gt;Often, some labs have easier alternatives. Either focus on a particular issue or run a workload and assess the whole situation.&lt;/p&gt;

&lt;h2 id=&quot;2-constraints-to-solve-labs&quot;&gt;2. Constraints to Solve Labs&lt;/h2&gt;

&lt;p&gt;As we progress throughout the courses, we start to have constraints to solve the labs. For example, “no index changes allowed” or “only query-level fixes.”&lt;/p&gt;

&lt;p&gt;But, the exercise I like the most is the “Let’s play being performance consultant.” We have to fix a workload under 30 minutes with as few changes as possible. The closest thing to a real-world emergency. That’s from Mastering Server Tuning again. My favorite course.&lt;/p&gt;

&lt;p&gt;Of course, there are more courses. They’re four in total. There’s one course solely on indexes, another one about query tuning, one to fix parameter sniffing issues, and, my favorite, the one on server-level fixes. Each course sits on top of the previous ones.&lt;/p&gt;

&lt;h2 id=&quot;3-popular-wisdom-and-guerilla-tactics&quot;&gt;3. Popular Wisdom and Guerilla Tactics&lt;/h2&gt;

&lt;p&gt;All over the courses, Brent shares his experience as a consultant. I have those tips and pieces of advice on my notes like &lt;em&gt;“when working with clients.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For example, he shares to build as few indexes as possible and provide rollback scripts for index creation, just in case. Also, to provide a prioritized list of actionable steps to make SQL Server fast.&lt;/p&gt;

&lt;p&gt;Also, he shares personal anecdotes. Like the day he went to consult wearing jeans, and everybody at the place was wearing jackets and ties. That story didn’t have a happy ending for the company. But, I won’t tell you more so you can find out what happened by taking the courses.&lt;/p&gt;

&lt;h2 id=&quot;parting-thought&quot;&gt;Parting Thought&lt;/h2&gt;

&lt;p&gt;Voilà! These are the three things I liked. My biggest lessons are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Focus all tuning efforts on the top-most wait type, and,&lt;/li&gt;
  &lt;li&gt;Make as few changes as possible to take you across the finish line.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Often, we start to push buttons and turn knobs expecting SQL Server to run faster, without noticeable improvements and making more harm than good.&lt;/p&gt;

&lt;p&gt;I will take the second lesson to other parts of my work, even outside of performance tuning context. Focus on the few changes that make the biggest impact.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To learn how to make your SQL Server go faster with Brent—and support this blog—buy &lt;a href=&quot;https://training.brentozar.com/p/recorded-class-season-pass-fundamentals?affcode=920087_fhe3khrq&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the Fundamentals Bundle here&lt;/a&gt; and the &lt;a href=&quot;https://training.brentozar.com/p/fundamentals-and-mastering-bundle?affcode=920087_fhe3khrq&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Fundamentals + Mastering Bundle here&lt;/a&gt;. I can’t recommend Brent’s courses enough. Learn from a real expert, with real queries.&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Blog, Error Messages and Recruiters</title>
   <link href="https://canro91.github.io/2022/04/25/MondayLinks/"/>
   <updated>2022-04-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/04/25/MondayLinks</id>
   <content type="html">&lt;h2 id=&quot;toxic-culture-is-driving-the-great-resignation&quot;&gt;Toxic Culture Is Driving the Great Resignation&lt;/h2&gt;

&lt;p&gt;I have read a couple of times about the Great Resignation. I guess the world isn’t the place after the pandemic. And that is reflected in the job market too. &lt;em&gt;“A toxic corporate culture is the single best predictor of which companies suffered from high attrition in the first six months of the Great Resignation.”&lt;/em&gt; &lt;a href=&quot;https://sloanreview.mit.edu/article/toxic-culture-is-driving-the-great-resignation/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;whats-in-a-good-error-message&quot;&gt;What’s in a Good Error Message?&lt;/h2&gt;

&lt;p&gt;I don’t know how many times I have debugged the “The given key was not present in the dictionary” error. In fact, I wrote &lt;a href=&quot;/2020/08/01/AnotherTwoCSharpIdiomsPart3/&quot;&gt;two C# idioms&lt;/a&gt; to avoid exceptions when working with dictionaries. From the article, to write better error messages: give context, show the actual error, and tell how to mitigate it. &lt;a href=&quot;https://www.morling.dev/blog/whats-in-a-good-error-message/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-dont-i-have-a-blog&quot;&gt;Why don’t I have a blog?&lt;/h2&gt;

&lt;p&gt;I have always heard: “start your own blog.” Well, I started my own blog…you’re reading it. But this is a post about the opposite: why not have a blog. The author points out is that posts only rephrase Reddit or StackOverflow’s comments. But, I learned from &lt;a href=&quot;/2020/10/01/ShowYourWorkTakeaways/&quot;&gt;Show Your Work&lt;/a&gt; that nothing is completely original. We all have some sort of inspiration. And, precisely that, and our own voice make our blogs unique. &lt;a href=&quot;https://fede.dm/blog/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1573484557933-fccf1ac71a30?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0MzIwNzc5Nw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;office workers circa 1940s&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@museumsvictoria?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Museums Victoria&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;learn-by-wrapping&quot;&gt;Learn By Wrapping&lt;/h2&gt;

&lt;p&gt;The best advice to learn something is to &lt;a href=&quot;/2020/05/07/PragmaticThinkingAndLearning/&quot;&gt;learn by doing&lt;/a&gt;. Finding project ideas is often difficult. Another TODO app, really? This article shares a learning activity: Write a wrapper for a library or an API you’re already familiar with. Instead of jumping to the documentation, start writing a wrapper in the language you want to learn. &lt;a href=&quot;https://mikerourke.dev/blog/learn-by-wrapping/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;career-advice-nobody-gave-me-never-ignore-a-recruiter&quot;&gt;Career Advice Nobody Gave Me: Never Ignore a Recruiter&lt;/h2&gt;

&lt;p&gt;These days, anyone with a LinkedIn profile and “Software engineer” in the headline is getting lots of spam messages and connection requests. I bet you already got a message between these lines: “Hi X, I have one company interested in your profile. Are you available for a quick call to share more details? Please, send me your CV at this email.”&lt;/p&gt;

&lt;p&gt;This article shares a template to reply back to these spammy messages. If you use it, you will be asking for a company name, seniority, and compensation before starting any conversation. That would be enough feedback for recruiters too. &lt;a href=&quot;https://index.medium.com/career-advice-nobody-gave-me-never-ignore-a-recruiter-4474eac9556&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another five reads! Do you answer spammy messages or connection requests on LinkedIn? Do you have a template to answer recruiters? What strategies do you use to learn new programming languages?&lt;/p&gt;

&lt;p&gt;Are you interested in unit testing? Check my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series. Don’t miss the previous Monday Links on &lt;a href=&quot;/2022/03/14/MondayLinks/&quot;&gt;Going solo, Making Friends and AutoMapper&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>BugOfTheDay: Object definitions, spaces, and checksums</title>
   <link href="https://canro91.github.io/2022/04/18/ObjectDefinitionsSpacesChecksums/"/>
   <updated>2022-04-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/04/18/ObjectDefinitionsSpacesChecksums</id>
   <content type="html">&lt;p&gt;These days I was working with a database migration tool and ended up spending almost a day figuring out why my migration didn’t work. This is what I learned after debugging an issue for almost an entire day.&lt;/p&gt;

&lt;p&gt;In one of my client’s projects to create or update stored procedures, we use a custom migrator tool, a wrapper on top of &lt;a href=&quot;https://github.com/DbUp/DbUp&quot;&gt;DbUp&lt;/a&gt;, “a set of .NET libraries that help you to deploy changes to different databases.” I’ve already written about &lt;a href=&quot;/2020/08/15/Simple.Migrations/&quot;&gt;Simple.Migrator&lt;/a&gt;, a similar tool.&lt;/p&gt;

&lt;p&gt;To avoid updating the wrong version of a stored procedure, we rely on checksums. Before updating a stored procedure, we calculate the checksum of the existing stored procedure using a command-line tool.&lt;/p&gt;

&lt;h2 id=&quot;how-to-find-object-definitions-in-sql-server&quot;&gt;How to find object definitions in SQL Server?&lt;/h2&gt;

&lt;p&gt;By the way… &lt;strong&gt;To find the text of a stored procedure in SQL Server, use the OBJECT_DEFINITION() function with the object id of the stored procedure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OBJECT_DEFINITION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dbo.MyCoolStoredProc&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;checksums-and-create-statements&quot;&gt;Checksums and CREATE statements&lt;/h2&gt;

&lt;p&gt;With the checksum of the stored procedure to update, we write a header comment in the new script file. Something like,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/*
Checksum: &quot;A-Sha1-here-of-the-previous-stored-proc&quot;
*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyCoolStoredProc&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;-- Beep, beep, boop...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To upgrade the database, the migrator compares the checksums of objects in the database with the checksums in header comments. If they’re different, the migrator displays a warning message and “fails fast.”&lt;/p&gt;

&lt;p&gt;Here comes the funny part. When I ran the migrator on my local machine, it always reported a difference. Even when I was grabbing the checksum from the migrator tool itself. Arrggg!&lt;/p&gt;

&lt;p&gt;After debugging for a while and &lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;isolating the problem&lt;/a&gt; I found something. On the previous script for the same stored procedure, I started the script with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE OR ALTER PROC&lt;/code&gt;. There’s nothing wrong with that.&lt;/p&gt;

&lt;p&gt;But there’s a difference in the object definitions of a stored procedure created with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE&lt;/code&gt; and with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE OR ALTER&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;create-proc-vs-create-or-alter-proc&quot;&gt;CREATE PROC vs CREATE OR ALTER PROC&lt;/h3&gt;

&lt;p&gt;Let me show you an example. Let’s create the same stored procedure with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE OR ALTER&lt;/code&gt; to see its object definition.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* With just CREATE */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_DEFINITION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dbo.Test&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Length&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OBJECT_DEFINITION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dbo.Test&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Text&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/* What about CREATE OR ALTER? */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_DEFINITION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dbo.Test&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Length&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OBJECT_DEFINITION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dbo.Test&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Text&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the output.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-18-ObjectDefinitionsSpacesChecksums/CreateVsCreateOrAlter.png&quot; alt=&quot;SQL Server object definitions&quot; width=&quot;500px&quot; /&gt;
    &lt;figcaption&gt;Object definition of a stored procedure with CREATE and CREATE OR ALTER&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Let’s notice the length of the two object definitions. They’re different! Some spaces were making my life harder. Arrrggg!&lt;/p&gt;

&lt;p&gt;The migrator compared checksums of the object definition from the database and the one in the header comment. They were different in some spaces. Spaces!&lt;/p&gt;

&lt;p&gt;I made the mistake of writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE OR ALTER&lt;/code&gt; on a previous migration, and the migrator didn’t take into account spaces in object names before creating checksums. I had to rewrite the previous script to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ALTER&lt;/code&gt; and recreate the checksums.&lt;/p&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting thoughts&lt;/h2&gt;

&lt;p&gt;But, what’s in this story for you? &lt;strong&gt;We should create processes to prevent mistakes in the first place.&lt;/strong&gt; For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/09/18/FormatSqlFilesOnCommit/&quot;&gt;scripts to commit SQL code formatted properly&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;validations to avoid applying data migrations to the wrong environment&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2020/09/02/TwoRecurringReviewComments/&quot;&gt;extensions or plugins to follow naming conventions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;scripts to install the right tools and dependencies to run a project&lt;/li&gt;
  &lt;li&gt;up to date documentation for internal tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you got the idea. It should be hard to make stupid mistakes.&lt;/p&gt;

&lt;p&gt;Often, &lt;a href=&quot;/2019/12/17/BetterCodeReviews/&quot;&gt;code reviews&lt;/a&gt; aren’t enough to enforce conventions and prevent mistakes. We’re humans and we all make mistakes. And, the more code someone reviews in a session, the more tired he will get. And, the more reviewers we add, the less effective the process gets.&lt;/p&gt;

&lt;p&gt;Voilà! That’s what I learned these days: read object definitions from SQL Server, polish my debugging skills, and build processes around our everyday development practice.&lt;/p&gt;

&lt;p&gt;For more content on SQL Server, check my other posts on &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;functions and WHERE clauses&lt;/a&gt;, &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;implicit conversions&lt;/a&gt;, and &lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;index recommendations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy debugging!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Working with ASP.NET Core IDistributedCache Provider for NCache</title>
   <link href="https://canro91.github.io/2022/04/11/DistributedCacheWithNCache/"/>
   <updated>2022-04-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/04/11/DistributedCacheWithNCache</id>
   <content type="html">&lt;p&gt;As we learned last time, when I covered &lt;a href=&quot;/2020/06/29/HowToAddACacheLayer/&quot;&gt;in-memory caching with ASP.NET Core&lt;/a&gt;, a cache is a storage layer between an application and an external resource (a database, for example) used to speed up future requests to that resource. In this post, let’s use ASP.NET Core &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDistributedCache&lt;/code&gt; abstractions to write a data caching layer using NCache.&lt;/p&gt;

&lt;h2 id=&quot;1-whats-ncache&quot;&gt;1. What’s NCache?&lt;/h2&gt;

&lt;p&gt;From &lt;a href=&quot;https://www.alachisoft.com/ncache/&quot;&gt;NCache official page&lt;/a&gt;, &lt;em&gt;“NCache is an Open Source in-memory distributed cache for .NET, Java, and Node.js applications.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Among other things, we can use NCache as a database cache, NHibernate 2nd-level cache, Entity Framework cache, and web cache for sessions and responses.&lt;/p&gt;

&lt;p&gt;NCache comes in three editions: Open Source, Professional, and Enterprise. The Open Source version supports up to two nodes and its cache server is only available for .NET Framework version 4.8. For a complete list of differences, check &lt;a href=&quot;https://www.alachisoft.com/ncache/edition-comparison.html&quot;&gt;NCache edition comparison&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the NCache key features is performance. Based on &lt;a href=&quot;https://www.alachisoft.com/ncache/ncache-performance-benchmarks.html&quot;&gt;their own benchmarks&lt;/a&gt;, “NCache can linearly scale to achieve 2 million operations per second with a 5-server cache cluster.”&lt;/p&gt;

&lt;h2 id=&quot;2-how-to-install-ncache-on-a-windows-machine&quot;&gt;2. How to install NCache on a Windows machine?&lt;/h2&gt;

&lt;p&gt;Let’s see how to install an NCache server on a Windows machine. For this, we need a Windows installer and have a &lt;a href=&quot;https://www.alachisoft.com/trial-key.html&quot;&gt;trial license key&lt;/a&gt;. Let’s install NCache Enterprise edition, version 5.2 SP1.&lt;/p&gt;

&lt;p&gt;After running the installer, we need to select the installation type from three options: Cache server, remote client, and Developer/QA. Let’s choose Cache Server.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/1-InstallationType.png&quot; alt=&quot;NCache Installation Types&quot; width=&quot;375px&quot; /&gt;
    &lt;figcaption&gt;Cache Server Installation Type&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then, we need to enter a license key. Let’s make sure to have a license key for the same version we’re installing. Otherwise, we will get an “invalid license key” error. We receive the license key in a message sent to the email address we used during registration.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/2-License.png&quot; alt=&quot;NCache License Key&quot; width=&quot;375px&quot; /&gt;
    &lt;figcaption&gt;Enter NCache license key&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next, we need to enter the full name, email, and organization we used to register ourselves while requesting the trial license.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/3-Data.png&quot; alt=&quot;Enter User Information&quot; width=&quot;375px&quot; /&gt;
    &lt;figcaption&gt;Enter User Information&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then, we need to select an IP address to bind our NCache server to. Let’s stick to the defaults.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/4-IP.png&quot; alt=&quot;Configure IP Binding&quot; width=&quot;375px&quot; /&gt;
    &lt;figcaption&gt;Configure IP Binding&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next, we need to choose an account to run NCache. Let’s use the Local System Account.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/5-User.png&quot; alt=&quot;Account to run NCache&quot; width=&quot;375px&quot; /&gt;
    &lt;figcaption&gt;Account to run NCache&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Once the installation finishes, our default browser will open with the Web Manager. By default, NCache has a default cache named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demoCache&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/6-Manager.png&quot; alt=&quot;NCache Web Manager&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;NCache Web Manager&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next time, we can fire the Web Manager by navigating to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8251&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;NCache’s official site recommends a minimum of two servers for redundancy purposes. But, for our sample app, let’s use a single-node server for testing purposes.&lt;/p&gt;

&lt;p&gt;So far, we have covered the installation instructions for a Windows machine. But, we can also install NCache in Linux and Docker containers. And, we can use NCache as virtual machines in Azure and AWS.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1501523460185-2aa5d2a0f981?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0ODc3ODIwOA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Storage unit&quot; /&gt;

&lt;figcaption&gt;A cache is a fast storage unit. Photo by &lt;a href=&quot;https://unsplash.com/@jezar?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Jezael Melgoza&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/warehouse?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-how-to-add-and-retrieve-data-from-an-ncache-cache&quot;&gt;3. How to add and retrieve data from an NCache cache?&lt;/h2&gt;

&lt;p&gt;Now, we’re ready to start using our NCache server from a .NET app. In Visual Studio, let’s create a solution with a .NET 6 “MSTest Test Project” and a class file to learn the basic caching operations with NCache.&lt;/p&gt;

&lt;h3 id=&quot;connecting-to-an-ncache-cache&quot;&gt;Connecting to an NCache cache&lt;/h3&gt;

&lt;p&gt;Before connecting to our NCache server, we need to first install the client NuGet package: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Alachisoft.NCache.SDK&lt;/code&gt;. Let’s use the version: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5.2.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To start a connection, we need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetCache()&lt;/code&gt; method with a cache name. For our sample app, let’s use the default cache: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demoCache&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s start writing a test to add and retrieve movies from a cache.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NCacheDemo.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NCacheTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;demoCache&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ICache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// We will fill in the details later&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you’re new to unit testing, start looking at &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;how to write your first unit test in C# with MSTest&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice we didn’t have to use a connection string to connect to our cache. We only used a cache name. The same one as in the Web Manager: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demoCache&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;NCache uses a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client.ncconf&lt;/code&gt; file instead of connection strings. We can define this file at the application or installation level. For our tests, we’re relying on the configuration file at the installation level. That’s why we only needed the cache name.&lt;/p&gt;

&lt;h3 id=&quot;adding-items&quot;&gt;Adding items&lt;/h3&gt;

&lt;p&gt;To add a new item to the cache, we need to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddAsync()&lt;/code&gt; methods with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CacheItem&lt;/code&gt; to cache. The key is an identifier and the item is a wrapper for the object to cache.&lt;/p&gt;

&lt;p&gt;Every item to cache needs an expiration. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CacheItem&lt;/code&gt; has an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Expiration&lt;/code&gt; property for that.&lt;/p&gt;

&lt;p&gt;There are two basic expiration types: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Absolute&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sliding&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A cached item with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Absolute&lt;/code&gt; expiration expires after a given time. Let’s say, a few seconds. But, an item with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sliding&lt;/code&gt; expiration gets renewed every time it’s accessed. If within the sliding time, we don’t retrieve the item, it expires.&lt;/p&gt;

&lt;p&gt;Let’s update our test to add a movie to our cache.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Runtime.Caching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NCacheDemo.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NCacheTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;demoCache&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToCacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;ICache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Let&apos;s add Titanic to the cache...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// We will fill in the details later&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheItem&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Expiration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Expiration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExpirationType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Serializable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, we used two helper methods: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToCacheKey()&lt;/code&gt; to create the key from every movie and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToCacheItem()&lt;/code&gt; to create a cache item from a movie.&lt;/p&gt;

&lt;p&gt;We used &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;records from C# 9.0&lt;/a&gt; to create our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; class. Also, we needed to annotate it with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Serializable]&lt;/code&gt; attribute.&lt;/p&gt;

&lt;h3 id=&quot;retrieving-items&quot;&gt;Retrieving items&lt;/h3&gt;

&lt;p&gt;After adding items, let’s retrieve them. For this, we need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get&amp;lt;T&amp;gt;()&lt;/code&gt; method with a key.&lt;/p&gt;

&lt;p&gt;Let’s complete our first unit test to retrieve the object we added.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Runtime.Caching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NCacheDemo.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NCacheTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;demoCache&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToCacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;ICache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Let&apos;s bring Titanic back...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cachedMovie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cachedMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheItem&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Expiration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Expiration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExpirationType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Serializable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;updating-items&quot;&gt;Updating items&lt;/h3&gt;

&lt;p&gt;If we try to add an item with the same key using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddAsync()&lt;/code&gt; methods, they will throw an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OperationFailedException&lt;/code&gt;. Try to add a unit test to prove that.&lt;/p&gt;

&lt;p&gt;To either add a new item or update an existing one, we should use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Insert()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InserAsync()&lt;/code&gt; methods instead. Let’s use them in another test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Runtime.Caching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NCacheDemo.Tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NCacheTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Our previous test is the same&lt;/span&gt;
    
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UpdateItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ICache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CacheManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;5th Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToCacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Let&apos;s add the 5th Element here...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedMovie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedCacheItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatedMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// There&apos;s already a cache item with the same key...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InsertAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedCacheItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cachedMovie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cacheKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updatedMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cachedMovie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Rest of the file...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InsertAsync()&lt;/code&gt; method to add an item with the same key. When we retrieved it, it contained the updated version of the item.&lt;/p&gt;

&lt;p&gt;There’s another basic method: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemoveAsync()&lt;/code&gt;. We can guess what they do. Again, try to write a test to prove that.&lt;/p&gt;

&lt;h2 id=&quot;4-how-to-use-aspnet-core-idistributedcache-with-ncache&quot;&gt;4. How to use ASP.NET Core IDistributedCache with NCache?&lt;/h2&gt;

&lt;p&gt;Up to this point, we have NCache installed and know how to add, retrieve, update, and remove items.&lt;/p&gt;

&lt;p&gt;Let’s revisit our sample application from our post about &lt;a href=&quot;/2020/06/29/HowToAddACacheLayer/&quot;&gt;using a Redis-powered cache layer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s remember the example from that last post. We had an endpoint that uses a service to access a database, but it takes a couple of seconds to complete. Let’s think of retrieving complex object graphs or doing some computations with the data before returning it.&lt;/p&gt;

&lt;p&gt;Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DistributedCacheWithNCache.Responses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DistributedCacheWithNCache.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SlowService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingSlowlyAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, boop...Aligning satellites...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Something&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SomeId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Anything&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we emulated a database call with a 3-second delay.&lt;/p&gt;

&lt;p&gt;Also, we wrote a set of extensions methods on top of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDistributedCache&lt;/code&gt; to add and retrieve objects from a cache.&lt;/p&gt;

&lt;p&gt;There were the extension methods we wrote last time,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Caching.Distributed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DistributedCacheWithNCache.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DistributedCacheExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DistributedCacheEntryOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultDistributedCacheEntryOptions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DistributedCacheEntryOptions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;AbsoluteExpirationRelativeToNow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SlidingExpiration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetOrSetValueAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDistributedCache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DistributedCacheEntryOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValueAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValueAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetValueAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDistributedCache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetStringAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeserializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetValueAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDistributedCache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DistributedCacheEntryOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetStringAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultDistributedCacheEntryOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we used Newtonsoft.Json to serialize and deserialize objects.&lt;/p&gt;

&lt;h3 id=&quot;ncache-and-the-idistributedcache-interface&quot;&gt;NCache and the IDistributedCache interface&lt;/h3&gt;

&lt;p&gt;Now, let’s use a .NET 6.0 “ASP.NET Core Web App,” those extension methods on top of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDistributedCache&lt;/code&gt;, and NCache to speed up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SlowService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, we need to install the NuGet package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NCache.Microsoft.Extensions.Caching&lt;/code&gt;. This package implements the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDistributedCache&lt;/code&gt; interface using NCache, of course.&lt;/p&gt;

&lt;p&gt;After installing that NuGet package, we need to add the cache into the ASP.NET dependencies container in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; file. To achieve this, we need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddNCacheDistributedCache()&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Program.cs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Alachisoft.NCache.Caching.Distributed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DistributedCacheWithNCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DistributedCacheWithNCache.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddNCacheDistributedCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We add the NCache implementation here...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;demoCache&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EnableLogs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionsEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddTransient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SlowService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, we continued to use the same cache name: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demoCache&lt;/code&gt;. And, also we relied on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deconstruct&lt;/code&gt; method to have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builder&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;services&lt;/code&gt; variables deconstructed. I took this idea from Khalid Abuhakmeh’s &lt;a href=&quot;https://khalidabuhakmeh.com/adding-clarity-to-dotnet-minimal-hosting&quot;&gt;Adding Clarity To .NET Minimal Hosting APIs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Back in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SlowService&lt;/code&gt;, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDistributedCache&lt;/code&gt; interface injected into the constructor and the extension methods in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistributedCacheExtensions&lt;/code&gt; class. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DistributedCacheWithNCache.Responses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Caching.Distributed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DistributedCacheWithNCache.Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SlowService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDistributedCache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SlowService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDistributedCache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_cache&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingSlowlyAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Here we wrap the DoSomethingAsync method around the cache logic&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetOrSetValueAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, boop...Aligning satellites...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Something&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SomeId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Anything&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, we wrapped the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoSomethingAsync()&lt;/code&gt; with actual retrieving logic around the caching logic in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetOrSetValueAsync()&lt;/code&gt;. At some point, we will have the same data in our caching and storage layers.&lt;/p&gt;

&lt;p&gt;With the caching in place, if we hit one endpoint that uses that service, we will see faster response times after the first call delayed by 3 seconds.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/8-SecondTime.png&quot; alt=&quot;Faster response times after using NCache&quot; width=&quot;900px&quot; /&gt;
    &lt;figcaption&gt;A few miliseconds reading it from NCache&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Also, if we go back to NCache Web Manager, we should see some activity in the server.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-11-DistributedCacheWithNCache/9-Dashboard.png&quot; alt=&quot;NCache Dashboard&quot; width=&quot;900px&quot; /&gt;
    &lt;figcaption&gt;NCache Dashboard showing our first request&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In this scenario, all the logic to add and retrieve items is abstracted behind the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDistributedCache&lt;/code&gt; interface. That’s why we didn’t need to directly call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get&amp;lt;T&amp;gt;()&lt;/code&gt; method. Although, if we take a look at the NCache source code, we will find those methods &lt;a href=&quot;https://github.com/Alachisoft/NCache/blob/b8bb6f06372c1f79397e95b3ca8612cff24eb911/SessionState/ASP.NET%20Core/NCacheSessionServices/NCacheDistributedCache/NCacheDistributedCache.cs#L132&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://github.com/Alachisoft/NCache/blob/b8bb6f06372c1f79397e95b3ca8612cff24eb911/SessionState/ASP.NET%20Core/NCacheSessionServices/NCacheDistributedCache/NCacheDistributedCache.cs#L111&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! That’s NCache and how to use it with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDistributedCache&lt;/code&gt; interface. With NCache, we have a distributed cache server with few configurations and a dashboard out-of-the-box. Also, we can add all the caching logic into &lt;a href=&quot;/2021/02/10/DecoratorPattern/&quot;&gt;decorators&lt;/a&gt; and have our services as clean as possible.&lt;/p&gt;

&lt;p&gt;To follow along with the code we wrote in this post, check my &lt;a href=&quot;https://github.com/canro91/NCacheDemo&quot;&gt;Ncache Demo&lt;/a&gt; repository over on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/NCacheDemo&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/NCacheDemo.svg&quot; alt=&quot;canro91/NCacheDemo - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To read more content, check my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series to learn from how to write your first unit tests to mocks. Also, check &lt;a href=&quot;/2022/08/08/FullTextSearchWithNCache/&quot;&gt;how to implement full-text searching with Lucene and NCache&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I wrote this post in collaboration with &lt;a href=&quot;https://www.alachisoft.com/&quot;&gt;Alachisoft&lt;/a&gt;, NCache creators.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: T-SQL doesn&apos;t have constants and variables aren&apos;t a good idea</title>
   <link href="https://canro91.github.io/2022/04/04/TSQLDoesNotHaveConstants/"/>
   <updated>2022-04-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/04/04/TSQLDoesNotHaveConstants</id>
   <content type="html">&lt;p&gt;Today I learned how to use constants in SQL Server stored procedures. While getting a stored procedure reviewed, I got one comment to remove literal values. This is how to bring constants in T-SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL Server doesn’t have a keyword for constants. To introduce constants in stored procedures, write literal values next to an explaining comment or use single-row views with the constant values as columns.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;1-dont-use-variables-as-constants&quot;&gt;1. Don’t use variables as constants&lt;/h2&gt;

&lt;p&gt;From C# and other programming languages, we’ve learned to use constants or enums instead of magic values all over our code. Often, we would like to bring constants to our T-SQL queries. But…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;T-SQL doesn’t have a keyword for constants. And SQL Server engine doesn’t inline variables when executing stored procedures.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first thing we try by mistake to emulate constants is to use variables.&lt;/p&gt;

&lt;p&gt;For example, let’s find all StackOverflow users with two reputation points. That’s not a popular reputation among StackOverflow users. We write something like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* An index to speed things up a bit */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usp_GetUsers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* This is how we often emulate constants */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the execution plan. Let’s keep an eye on the number of estimated users.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-04-TSQLDoesNotHaveConstants/Variable.png&quot; alt=&quot;StackOverflow users with reputation = 2&quot; /&gt;
    &lt;figcaption&gt;Execution plan of finding users with 2-point reputation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;But, there’s a downside. Variables inside stored procedures trigger a different behavior in SQL Server.&lt;/p&gt;

&lt;h3 id=&quot;variables-and-execution-plans&quot;&gt;Variables and execution plans&lt;/h3&gt;

&lt;p&gt;When executing a stored procedure, SQL Server creates an execution plan for the first set of parameters it sees. And, the next time we run that stored procedure, SQL Server reuses the same execution plan, even if we use different parameters. We call this behavior &lt;strong&gt;Parameter Sniffing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SQL Server uses statistics (histograms built from samples of our data) to choose the shape of execution plans. SQL Server has to choose the first table to read, the number of threads, and the amount of memory, among other things.&lt;/p&gt;

&lt;p&gt;But, when there are variables in a stored procedure, SQL Server builds execution plans, not from statistics (samples of our data), but from an “average value.”&lt;/p&gt;

&lt;p&gt;Variables make SQL Server build different execution plans, probably not suited for the set of parameters we’re calling our stored prcedures with. That’s why variables aren’t a good idea to replace constants.&lt;/p&gt;

&lt;h2 id=&quot;2-literal-values-and-comments&quot;&gt;2. Literal values and comments&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The simplest solution to constants in T-SQL is to use literal values.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To make stored procedures more maintainable, it’s a good idea to write an explaining comment next to the literal value.&lt;/p&gt;

&lt;p&gt;Let’s rewrite our stored procedure with a literal and a comment.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usp_GetUsers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* Interesting reputation */&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the execution plan.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-04-04-TSQLDoesNotHaveConstants/LiteralAndComment.png&quot; alt=&quot;StackOverflow users with reputation = 2&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;This time, we&apos;re back to a literal value and a comment&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Do you remember the estimated number of users from our example with variables? It was 123 users. Now, we have a more accurate estimated number. It’s 1,854 users. SQL Server isn’t using an average value anymore. It has better estimates this time!&lt;/p&gt;

&lt;p&gt;We even have an index recommendation in our execution plan. By the way, &lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;don’t blindly follow index recommendations&lt;/a&gt;, just listening to them. They’re only a list of columns to consider indexing.&lt;/p&gt;

&lt;h2 id=&quot;3-create-a-view-for-constants&quot;&gt;3. Create a view for constants&lt;/h2&gt;

&lt;p&gt;The hardcoded value and an explanatory comment are OK if we have our “constant” in a few places.&lt;/p&gt;

&lt;p&gt;A more maintainable solution to literal values is to create a single-row view with columns named after the constants to declare.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;VIEW&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vw_Constant&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InterestingReputation&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With that view in place, we can replace the hardcoded values in our stored procedure.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usp_GetUsers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* The view with our constant */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vw_Constant&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterestingReputation&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A more maintainable alternative while keeping good estimates.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to use constants with a view in SQL Server. I found a proposal to introduce &lt;a href=&quot;https://blog.greglow.com/2020/03/05/sql-t-sql-really-needs-constants/&quot;&gt;a constant keyword&lt;/a&gt; in SQL Server. I learned about the trick with views from this &lt;a href=&quot;https://stackoverflow.com/questions/26652/is-there-a-way-to-make-a-tsql-variable-constant&quot;&gt;StackOverflow question&lt;/a&gt; and in &lt;a href=&quot;https://stackoverflow.com/questions/6114826/sql-views-no-variables&quot;&gt;this one&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;For more content on SQL Server, check my other posts on &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;functions and WHERE clauses&lt;/a&gt;, &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;implicit conversions&lt;/a&gt; and &lt;a href=&quot;/2022/02/21/CaseSensitiveSearchSQLServer/&quot;&gt;case-sensitive searches&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SQL Server Index recommendations: Just listen to them</title>
   <link href="https://canro91.github.io/2022/03/21/SQLServerIndexRecommendations/"/>
   <updated>2022-03-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/03/21/SQLServerIndexRecommendations</id>
   <content type="html">&lt;p&gt;I guess you have seen SQL Server index recommendations on actual execution plans. But, you shouldn’t take them too seriously. This is what I learned about SQL Server index recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL Server builds index recommendations based on the WHERE and SELECT clauses of a query, without considering GROUP BY or ORDER BY clauses. Use index recommendations as an starting point to craft better indexes.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;whats-a-nonclustered-index-anyways&quot;&gt;What’s a nonclustered index anyways?&lt;/h2&gt;

&lt;p&gt;If you’re wondering… &lt;strong&gt;A nonclustered index is a redundant, sorted, and smaller copy of one table to make queries go faster.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you want to find a particular book in a library. Would you go shelve by shelve, book by book until you find it? Or would you use the library catalog to go directly to the one you want? Well, these days, I guess libraries have software for that. But that’s the same idea. That library catalog or reference software works like an index.&lt;/p&gt;

&lt;p&gt;After that aside…&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1465929639680-64ee080eb3ed?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYyODI5MzQxOQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Rijksmuseum, Amsterdam, Netherlands&quot; /&gt;

&lt;figcaption&gt;Rijksmuseum, Amsterdam, Netherlands. Photo by &lt;a href=&quot;https://unsplash.com/@willvanw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Will van Wingerden&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The next time you see an index recommendation on actual execution plans or from the Tuning Advisor, don’t rush to create it. Just listen to them!&lt;/p&gt;

&lt;p&gt;To prove why we shouldn’t blindly create every index recommendation, let’s use the StackOverflow database to write queries and indexes.&lt;/p&gt;

&lt;h2 id=&quot;index-recommendations-and-scans&quot;&gt;Index recommendations and Scans&lt;/h2&gt;

&lt;p&gt;Let’s start with no indexes and a simple query to find all users from Colombia. Let’s check the actual execution plan.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the execution plan. Do you see any index recommendations? Nope!&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-21-SQLServerIndexRecommendations/UsersFromColombia-NoRecommendation.png&quot; alt=&quot;Users from Colombia with no index recommendation&quot; width=&quot;500px&quot; /&gt;
    &lt;figcaption&gt;Execution plan of finding all users from Colombia&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;If SQL Server has to scan the object, the actual execution plan won’t show any index recommendations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s change the query a bit. Let’s find the first 1,000 users from Colombia ordered by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reputation&lt;/code&gt; instead.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, we have the missing index recommendation.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-21-SQLServerIndexRecommendations/UsersFromColombia-Recommendation.png&quot; alt=&quot;Users from Colombia with an index recommendation&quot; /&gt;
    &lt;figcaption&gt;Ladies and gentlemen, now the index recommendation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For that query, SQL Server suggests an index on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DisplayName&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reputation&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-21-SQLServerIndexRecommendations/UsersFromColombia-RecommendedIndex.png&quot; alt=&quot;Recommeded index for Users from Colombia&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;The recommended index&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Indexes aren’t sorted by included columns. Indexes might have some columns appended or “included” to avoid looking back to the table to access them.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;index-recommendations-including-all-the-columns&quot;&gt;Index recommendations including all the columns&lt;/h2&gt;

&lt;p&gt;To point out the next reason not to blindly add recommended indexes, let’s change our query to bring all columns instead of four of them.&lt;/p&gt;

&lt;p&gt;Don’t write SELECT * queries, by the way.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s see what the execution plan looks like.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-21-SQLServerIndexRecommendations/UsersFromColombia-AllColumns.png&quot; alt=&quot;Execution plan for a &apos;SELECT *&apos;&quot; width=&quot;700px&quot; /&gt;
    &lt;figcaption&gt;Execution plan for a &apos;SELECT *&apos;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;At first glance, the plan looks similar. But let’s focus on what changed on the recommended index. Here it is.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-21-SQLServerIndexRecommendations/RecommendedIndex-AllColumns.png&quot; alt=&quot;Recommended index with all columns in the Users table&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Recommended index with all columns in the Users table&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;SQL Server recommended an index with all the columns in the table, even NVARCHAR(MAX) columns. Arrrggg!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Often, SQL Server recommends adding all the columns from the table into the INCLUDE part of indexes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Indexes aren’t free. They take disk space. Even included columns take disk space. The more keys and included columns, the bigger the indexes get and the longer SQL Server will hold locks to insert, update, and delete data.&lt;/p&gt;

&lt;h2 id=&quot;index-recommendations-and-order-bys&quot;&gt;Index Recommendations and ORDER BY’s&lt;/h2&gt;

&lt;p&gt;The next thing to know about index recommendations has to do with the keys in the index.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL Server index recommendations are based on the WHERE and SELECT clauses. SQL Server doesn’t use GROUP BY or ORDER BY clauses to build index recommendations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For our last query, let’s add the recommended index (without any included columns) and another one with the ORDER BY in mind. These are the two new indexes,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* This is the recommended one */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/* This one has Reputation, which is on the ORDER BY */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Location_Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After creating these indexes, let’s run our query again,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, the execution plan looks like this,&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-21-SQLServerIndexRecommendations/RecommendedVsCustom.png&quot; alt=&quot;Recommended index with all columns in the Users table&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Recommended index with all columns in the Users table&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;SQL Server recommended one index but used another, even when the recommended index was in place.&lt;/p&gt;

&lt;p&gt;SQL Server only looks at the WHERE and SELECT clauses of queries to build recommendations. We can create better indexes than the recommended ones for queries with ORDER BY and GROUP BY clauses.&lt;/p&gt;

&lt;h2 id=&quot;recommended-indexes-and-key-order&quot;&gt;Recommended indexes and key order&lt;/h2&gt;

&lt;p&gt;Next, let’s dig into the order of keys in recommended indexes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keys on recommended indexes are based on equality and inequality comparisons on the WHERE clause. Columns with equality comparisons are shown first, followed by columns with inequality comparisons&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s add another comparison to our sample query. This time, let’s look for users from Colombia with more than 10 reputation points.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s check the recommended index on the execution plan.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-21-SQLServerIndexRecommendations/EqualityAndInequality.png&quot; alt=&quot;Recommended index with all columns in the Users table&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Recommended index on Location followed by Reputation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The recommended index contains the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; column first, then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reputation&lt;/code&gt; column. But, in the query, the filter on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reputation&lt;/code&gt; was first. What happened here?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL Server builds recommended indexes on equality comparisons followed by inequality comparisons.&lt;/strong&gt; That’s why an apparent mismatch in the order of keys on the index and filters on the query.&lt;/p&gt;

&lt;h2 id=&quot;dont-blindly-create-recommended-indexes&quot;&gt;Don’t blindly create recommended indexes&lt;/h2&gt;

&lt;p&gt;Last thing about recommended indexes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index recommendations don’t take into account existing indexes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s check our existing indexes and see if we can combine the recommended indexes with our existing ones. If our existing indexes overlap with the recommended ones, let’s drop the old ones.&lt;/p&gt;

&lt;p&gt;Let’s build as few indexes as possible to support our queries. Let’s keep around 5 indexes per table with around 5 columns per index.&lt;/p&gt;

&lt;h2 id=&quot;parting-words&quot;&gt;Parting words&lt;/h2&gt;

&lt;p&gt;Voilà! These are some of the things I learned about SQL Server index recommendations. Remember, indexes aren’t free. The more indexes we add, the slower our queries will get.&lt;/p&gt;

&lt;p&gt;Next time we see index recommendations on our execution plans, let’s check if we already have a similar index and modify that one. If we don’t, please let’s remember to at least change the recommended index’s name and not to include all the columns of our table.&lt;/p&gt;

&lt;p&gt;I learned these things following &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar’s Master Index Tuning&lt;/a&gt; class. Great class!&lt;/p&gt;

&lt;p&gt;For more content on SQL Server, check &lt;a href=&quot;/2022/02/21/CaseSensitiveSearchSQLServer/&quot;&gt;how to do case sensitive searches&lt;/a&gt;, &lt;a href=&quot;/2022/03/07/OptimizeGroupBySQLServer/&quot;&gt;how to optimize GROUP BY queries&lt;/a&gt; and &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;what implicit conversions are&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Going solo, making friends and AutoMapper</title>
   <link href="https://canro91.github.io/2022/03/14/MondayLinks/"/>
   <updated>2022-03-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/03/14/MondayLinks</id>
   <content type="html">&lt;h2 id=&quot;11-things-i-wish-i-knew-before-i-went-independent&quot;&gt;11 things I wish I knew before I went Independent&lt;/h2&gt;

&lt;p&gt;A good read for all of us wanting to go solo. The author answers the question: &lt;em&gt;“how you made the decision to go independent and how you got started?”&lt;/em&gt;. Among other things: find a niche, learn about sales and marketing, and talk to your customers… Definitively we don’t have to be the “I do anything for anyone” kind of guys. &lt;a href=&quot;https://georgestocker.com/2021/07/06/stuff-i-wish-i-knew-before-i-went-independent/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If you don’t want to be just another pair of hands, the first thing you need to figure out is: Who do you want to serve?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;how-to-build-remote-teams-properly&quot;&gt;How to build remote teams properly&lt;/h2&gt;

&lt;p&gt;I remember my first day at one past job. I spent like one week going to the office and doing nothing. I couldn’t even touch a computer until someone, who was on vacation, showed me all the security policies.&lt;/p&gt;

&lt;p&gt;A clear onboarding is much more important these days of work-from-home than before. Have a standardized onboarding process and assign a buddy to the newcomers. Onboarding is only one step to building remote teams. &lt;a href=&quot;https://vadimkravcenko.com/en/how-to-build-remote-teams-properly/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1479480374076-c5929dddcc47?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0MDIxMjE4NA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;vintage cafe scene&quot; /&gt;

&lt;figcaption&gt;Let&apos;s have a coffee and be friends...Photo by &lt;a href=&quot;https://unsplash.com/@lesanderson?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Les Anderson&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;this-is-how-to-make-friends-as-an-adult&quot;&gt;This Is How To Make Friends As An Adult&lt;/h2&gt;

&lt;p&gt;Let’s switch gears for this one. Speaking of being adults and making friends…&lt;em&gt;“being ‘close’ means a text message twice a year.”&lt;/em&gt; That got me reading. Reconnect with old friends, listen to people and ask them to tell more, check in every two weeks, and join a group. &lt;a href=&quot;https://www.bakadesuyo.com/2017/02/how-to-make-friends-as-an-adult/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-i-decided-to-ditch-automapper&quot;&gt;Why I Decided to Ditch AutoMapper&lt;/h2&gt;

&lt;p&gt;This is an oldie. I have a love-and-hate relationship with AutoMapper. I can’t debug and easily find where the mappings are. Maybe, because in one of my client’s projects we use AutoMapper when we shouldn’t.&lt;/p&gt;

&lt;p&gt;I liked that the AutoMapper’s author replied in the comments. He wrote, &lt;em&gt;“use AutoMapper if you can Auto-Map 75% or more in a given mapping. If not, don’t use it. Just map things manually.”&lt;/em&gt; &lt;a href=&quot;https://jacobian.org/2021/dec/7/wst-byoc/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links, a bit diverse this time. How is the onboarding process at your job? Do you use AutoMapper? If not, what do you use instead? Stay tuned to the upcoming Monday Links.&lt;/p&gt;

&lt;p&gt;In the meantime, check my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series. Don’t miss the previous Monday Links on &lt;a href=&quot;/2022/01/17/MondayLinks/&quot;&gt;Better programming, flags, and C#&lt;/a&gt; and &lt;a href=&quot;/2022/02/14/MondayLinks/&quot;&gt;Daily Meetings, Estimates, and Challenges&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to optimize Group by queries in SQL Server</title>
   <link href="https://canro91.github.io/2022/03/07/OptimizeGroupBySQLServer/"/>
   <updated>2022-03-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/03/07/OptimizeGroupBySQLServer</id>
   <content type="html">&lt;p&gt;Let me share this technique I learned to improve queries with GROUP BY in SQL Server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To improve queries with GROUP BY, write the SELECT query with the GROUP BY part using only the needed columns to do the grouping or sorting inside a common table expression (CTE) first. Then, join the CTE with the right tables to find other columns.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;usual-group-by-find-stackoverflow-most-down-voted-questions&quot;&gt;Usual GROUP BY: Find StackOverflow most down-voted questions&lt;/h2&gt;

&lt;p&gt;Let’s use this technique to tune the store procedure to find &lt;a href=&quot;http://data.stackexchange.com/stackoverflow/query/36660/most-down-voted-questions&quot;&gt;most down-voted questions on StackOverflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the store procedure. Let’s fire our local copy of the &lt;a href=&quot;https://www.brentozar.com/archive/2021/03/download-the-current-stack-overflow-database-for-free-2021-02/&quot;&gt;StackOverflow 2013 database&lt;/a&gt; to run it.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MostDownVotedQuestions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;top&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Vote count&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Votes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;inner&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Posts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PostTypeId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VoteTypeId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Vote count&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;desc&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I ran this stored procedure on my local machine  five times without any indexes. It took about 2 seconds each time. On my machine, SQL Server only had 8GB of RAM. Remember, by default &lt;a href=&quot;/2020/11/13/SQLServerMemory/&quot;&gt;SQL Server uses all available RAM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the execution plan. Let’s notice the Clustered Index Seek on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Posts&lt;/code&gt; table. And, yes, SQL Server is recommending an index. But we’re not adding it.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-07-OptimizeGroupBySQLServer/ExecutionPlan-Before.png&quot; alt=&quot;StackOverflow most down-votes questions&quot; /&gt;
    &lt;figcaption&gt;StackOverflow most down-votes questions&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then, these are the metrics grabbed with &lt;a href=&quot;https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit#sp_blitzcache-find-the-most-resource-intensive-queries&quot;&gt;sp_BlitzCache&lt;/a&gt; from the First Responder Kit. This stored procedure finds all the most CPU intensive queries SQL Server has recently executed.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-07-OptimizeGroupBySQLServer/spBlitzCacheBefore.png&quot; alt=&quot;Most CPU intensive queries&quot; /&gt;
    &lt;figcaption&gt;Most CPU intensive queries&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;To find the most down-voted questions, our stored procedure is grouping by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt;. And, that’s an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NVARCHAR(MAX)&lt;/code&gt; column, the actual content of StackOverflow posts.&lt;/p&gt;

&lt;p&gt;Sorting and grouping on large data types is a CPU expensive operation.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1509358271058-acd22cc93898?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYyNjk4NzY5MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Spoons full of Indian spices&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@pratiksha_mohanty?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Pratiksha Mohanty&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;group-and-order-inside-ctes-first&quot;&gt;Group and order inside CTE’s first&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;To improve queries with GROUP BY, group inside a common table expression (CTE) with only the required columns in the grouping. For example, IDs or columns covered by indexes. Then, join the CTE with the right tables to find other columns.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After grouping only by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PostId&lt;/code&gt; inside a CTE first, our stored procedure looks like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MostDownVotedQuestions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MostDownVoted&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;top&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VoteCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* We removed the Body column */&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Votes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;inner&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Posts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PostTypeId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VoteTypeId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* Also, from here */&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VoteCount&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;desc&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VoteCount&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Vote count&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MostDownVoted&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;inner&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Posts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostId&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we are excluding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt; column from the GROUP BY part. Then, we are joining the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MostDownVotes&lt;/code&gt; CTE to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Post&lt;/code&gt; table to show only the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Body&lt;/code&gt; of the 20 resulting posts.&lt;/p&gt;

&lt;p&gt;Again, this is the execution plan of grouping inside a CTE first.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-07-OptimizeGroupBySQLServer/ExecutionPlanAfter.png&quot; alt=&quot;StackOverflow most down-votes questions&quot; /&gt;
    &lt;figcaption&gt;StackOverflow most down-votes questions grouping inside a CTE&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice the Clustered Index Seek operator on the left branch. That’s to find the body of only the 20 post SQL Server found as a result of grouping inside the CTE. This time, SQL Server is grouping and sorting fewer data. It made our stored procedure use less CPU time.&lt;/p&gt;

&lt;p&gt;Let’s take another look at sp_BlitzCache. Before running the modified version of our store procedure five times, I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DBCC FREEPROCCACHE&lt;/code&gt; to free up SQL Server’s plan cache.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-03-07-OptimizeGroupBySQLServer/spBlitzCacheAfter.png&quot; alt=&quot;Most CPU intensive queries&quot; /&gt;
    &lt;figcaption&gt;Most CPU intensive queries with our modified stored proc&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice the “Total CPU” and “Avg CPU” columns, we’re using less CPU time after the change. I went from 36.151ms of total CPU time to 35.505ms. Hooray!&lt;/p&gt;

&lt;p&gt;Now, imagine if that store procedure runs not only 5 times, but multiple times per minute. What if our stored procedure feeds a reporting screen in our app? That change with a CTE will make a huge difference in the overral CPU usage.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how we can improve queries with GROUP BY. Remember, group and sort inside CTE’s to take advantage of existing indexes and avoid expensive sorting operations. Use this technique with OFFSET/FETCH, SUM, AVG, and other group functions.&lt;/p&gt;

&lt;p&gt;I learned this technique following &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar’s Mastering Query Tuning&lt;/a&gt; class.&lt;/p&gt;

&lt;p&gt;For more SQL Server content, check &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;don’t write functions around columns in WHERE&lt;/a&gt;, &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;what are implicit conversions?&lt;/a&gt;, and &lt;a href=&quot;/2022/02/21/CaseSensitiveSearchSQLServer/&quot;&gt;How to do a case-sensitive search in SQL Server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to do a case-sensitive search in SQL Server</title>
   <link href="https://canro91.github.io/2022/02/21/CaseSensitiveSearchSQLServer/"/>
   <updated>2022-02-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/02/21/CaseSensitiveSearchSQLServer</id>
   <content type="html">&lt;p&gt;Do you use LOWER or UPPER to do case-sensitive searches? Let’s see how to write a case-sensitive search in SQL Server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write a case-sensitive search in SQL Server, don’t use UPPER or LOWER functions around the column to filter. Instead, use the COLLATE keyword with a case-sensitive collation followed by the comparison.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;naive-case-sensitive-search&quot;&gt;Naive case sensitive search&lt;/h2&gt;

&lt;p&gt;Often by mistake, to do a case-sensitive search, we wrap a column around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOWER()&lt;/code&gt;. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LOWER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;john%&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--    ^^^^^&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We tried to find the first 50 StackOverflow users with DisplayName containing lowercase ‘john’.&lt;/p&gt;

&lt;p&gt;But, interestingly enough, some results don’t match our intended filter. They include both lowercase and uppercase ‘john’.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-02-21-CaseSensitiveSearchSQLServer/LikeWithLower.png&quot; alt=&quot;Result of finding users by wrapping a column with LOWER&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Naive case insensitive search using LOWER&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It would be the same if we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPPER()&lt;/code&gt; instead. In general, &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;let’s not use functions around columns in our WHEREs&lt;/a&gt;. That’s a common bad practice.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1597742200037-aa4d64d843be?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjI0ODU2MzAz&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;scrabble pieces&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@brett_jordan?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Brett Jordan&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/abc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;collation-and-case-sensitivity&quot;&gt;Collation and case sensitivity&lt;/h2&gt;

&lt;p&gt;In SQL Server, collations provide sorting rules and case and accent sensitivity for our data. For example, when we use an ORDER BY, the sort order of our results depends on the database collation.&lt;/p&gt;

&lt;p&gt;In SQL Server Management Studio, we find a database collation after clicking on the database Properties and then on General. It’s under the Maintenance section.&lt;/p&gt;

&lt;p&gt;Here’s mine.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-02-21-CaseSensitiveSearchSQLServer/DatabaseProperties.png&quot; alt=&quot;SQL Server database properties&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;SQL Server database properties&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;The default collation for English is SQL_Latin1_General_CP1_CI_AS. This is a case-insensitive collation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In collation names, CI means case insensitive. And, AS means accent sensitive.&lt;/p&gt;

&lt;p&gt;That’s why when we wrote &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOWER(DisplayName)&lt;/code&gt;, SQL Server showed uppercase and lowercase results. SQL Server used the default collation, which was a case-insensitive one.&lt;/p&gt;

&lt;p&gt;For more details about collations, check Microsoft docs on &lt;a href=&quot;https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-ver15&quot;&gt;collations and Unicode support&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;case-sensitive-searches-in-where-clause&quot;&gt;Case sensitive searches in WHERE clause&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For case-sensitive searches in SQL Server, use COLLATE keyword with the case sensitive collation “SQL_Latin1_General_CP1_CS_AS” followed by the LIKE or = comparisons as usual.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using the SQL_Latin1_General_CP1_CS_AS collation, a is different from A, and à is different from á. It’s both case and accent-sensitive.&lt;/p&gt;

&lt;p&gt;Let’s rewrite our query with COLLATE,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COLLATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SQL_Latin1_General_CP1_CS_AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%john&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we have the results we were expecting. Only users with lowercase ‘john’ in their display name.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-02-21-CaseSensitiveSearchSQLServer/CaseSensitive.png&quot; alt=&quot;Query to find StackOverflow users with COLLATE&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Case sensitive query with COLLATE&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Voilà! That’s how we can write case-sensitive searches in SQL Server. Remember, don’t use LOWER or UPPER. They won’t work for case-sensitive searches. Use a different collation instead.&lt;/p&gt;

&lt;p&gt;For more content on SQL Server, check &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;what are implicit conversions and why you should care&lt;/a&gt;, &lt;a href=&quot;/2022/03/07/OptimizeGroupBySQLServer/&quot;&gt;how to optimize GROUP BY queries&lt;/a&gt;, and &lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;just listen to index recommendations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Daily Meetings, Estimates, and Challenges</title>
   <link href="https://canro91.github.io/2022/02/14/MondayLinks/"/>
   <updated>2022-02-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/02/14/MondayLinks</id>
   <content type="html">&lt;p&gt;This Monday Links episode is about careers and developer life. Four reads. Let’s start with a rant on daily meetings. Enjoy!&lt;/p&gt;

&lt;h2 id=&quot;daily-standup-meetings-are-useless&quot;&gt;Daily Standup Meetings are useless&lt;/h2&gt;

&lt;p&gt;I think I could have written this article myself. I’ve been in those daily meetings you want to finish as quickly as possible to get back to your life. -“I’m working on ABC, no blockers.” We need to stop doing daily meetings as a micromanaging strategy. Otherwise, let’s replace them with an email or a message in a group chat. That would be more productive for everyone. &lt;a href=&quot;https://dev.to/dvddpl/daily-standup-meetings-are-useless-1kie&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;is-tasking-developers-with-creating-detailed-estimates-a-waste-of-company-money&quot;&gt;Is tasking developers with creating detailed estimates a waste of company money?&lt;/h2&gt;

&lt;p&gt;I’ve worked in places where developers spend half a day estimating tasks and, they have to resend their estimates every time they change them. And, I’ve heard stories from past coworkers where they spend a whole day estimating tasks for the next 6-month period. This is a common thing and I don’t see an easy way out because “that’s how we have always done things here”.&lt;/p&gt;

&lt;p&gt;Quoting the article about why project management techniques don’t work on software projects… &lt;em&gt;“software development involves messy discovery of new tasks in the complex and abstract environment of code, which results in software development task durations being a ‘?’.”&lt;/em&gt; &lt;a href=&quot;https://iism.org/article/is-tasking-developers-with-creating-detailed-estimates-a-waste-of-company-money-42&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1578988247625-4e87a4a56afa?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzODU2ODE0MQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Film Examining Room circa 1940s&quot; /&gt;

&lt;figcaption&gt;It&apos;s time to quit their job? Photo by &lt;a href=&quot;https://unsplash.com/@museumsvictoria?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Museums Victoria&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/TVe0IEdsVc8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;5-signs-its-time-to-quit-your-job&quot;&gt;5 Signs It’s Time to Quit Your Job&lt;/h2&gt;

&lt;p&gt;Are you planning to change jobs? This article can help you to decide. One common factor is learning. Ask yourself if you’re learning new skills and if your learning is still valuable in another company. Quoting the article &lt;em&gt;“Sometimes five years of experience is just… the same year of experience, five times over.”&lt;/em&gt; &lt;a href=&quot;https://cate.blog/2021/11/29/5-signs-its-time-to-quit-your-job/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;bring-your-own-code&quot;&gt;Bring your own code&lt;/h2&gt;

&lt;p&gt;Have you ever been in a hiring process where you are asked to work on a coding exercise without getting clear instructions? This article shows an alternative to coding exercises: bring an already-written piece of code. I like this idea. Also, it shows hiring managers what to look for. Hiring managers…&lt;em&gt;“you need to be comfortable with the fact that you’ll lose some candidates who are unwilling to do your assignment but would take a different option.”&lt;/em&gt; &lt;a href=&quot;https://jacobian.org/2021/dec/7/wst-byoc/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another Monday Links about career and workplaces. I don’t know if this topic will become a trend in future “Monday Links.” Stay tuned to find it out.&lt;/p&gt;

&lt;p&gt;In the meantime, check my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series. Don’t miss the previous Monday Links on &lt;a href=&quot;/2022/01/17/MondayLinks/&quot;&gt;Better programming, flags, and C#&lt;/a&gt; and &lt;a href=&quot;/2021/11/01/MondayLinks/&quot;&gt;Workplaces, studying and communication&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What are implicit conversions and why you should care</title>
   <link href="https://canro91.github.io/2022/02/07/WhatAreImplicitConversions/"/>
   <updated>2022-02-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/02/07/WhatAreImplicitConversions</id>
   <content type="html">&lt;p&gt;SQL Server compares columns and parameters with the same data types. But, if the two data types are different, weird things happen. Let’s see what implicit conversions are and why we should care.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An implicit conversion happens when the data types of columns and parameters in comparisons are different. And SQL Server has to convert between them, following type precedence rules. Often, implicit conversions lead to unneeded index or table scans.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;an-implicit-conversion-that-scans&quot;&gt;An implicit conversion that scans&lt;/h2&gt;

&lt;p&gt;Let’s see an implicit convention! For this, let’s create a new table from the StackOverflow &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Users&lt;/code&gt; table. But, this time, let’s change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; data type from NVARCHAR to VARCHAR.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StackOverflow2013&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users_Varchar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CLUSTERED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users_Varchar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users_Varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s find all users from Colombia. To prove a point, let’s query the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Users_Varchar&lt;/code&gt; table instead of the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Users&lt;/code&gt; table.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users_Varchar&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* The column is VARCHAR, but the parameter NVARCHAR */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we have declared &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Location&lt;/code&gt; as NVARCHAR. We have a type mismatch between the column and the variable.&lt;/p&gt;

&lt;p&gt;Let’s see the execution plan.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-02-07-WhatAreImplicitConversions/IndexScanOnLocation.png&quot; alt=&quot;Execution plan of finding all users StackOverflow users from Colombia&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;StackOverflow users from Colombia&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;SQL Server had to scan the index on Location. But, why?&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-02-07-WhatAreImplicitConversions/WarningSign.png&quot; alt=&quot;Warning sign on execution plan of finding all users StackOverflow users from Colombia&quot; width=&quot;500px&quot; /&gt;
    &lt;figcaption&gt;Warning sign on SELECT operator&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Let’s notice the warning sign on the execution plan. When we hover over it, it shows the cause: SQL Server had to convert the two types. Yes! SQL Server converted between VARCHAR and NVARCHAR.&lt;/p&gt;

&lt;h2 id=&quot;sql-server-data-type-precedence&quot;&gt;SQL Server data type precedence&lt;/h2&gt;

&lt;p&gt;To determine what types to convert, SQL Server follows a data type precedence order. This is a short version:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;datetimeoffset&lt;/li&gt;
  &lt;li&gt;datetime2&lt;/li&gt;
  &lt;li&gt;datetime&lt;/li&gt;
  &lt;li&gt;smalldatetime&lt;/li&gt;
  &lt;li&gt;date&lt;/li&gt;
  &lt;li&gt;time&lt;/li&gt;
  &lt;li&gt;decimal&lt;/li&gt;
  &lt;li&gt;bigint&lt;/li&gt;
  &lt;li&gt;int&lt;/li&gt;
  &lt;li&gt;timestamp&lt;/li&gt;
  &lt;li&gt;uniqueidentifier&lt;/li&gt;
  &lt;li&gt;nvarchar (including nvarchar(max))&lt;/li&gt;
  &lt;li&gt;varchar (including varchar(max))&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;SQL Server convert “lower” types to “higher” types. We don’t need to memorize this order. Let’s remember SQL Server always has to convert VARCHAR to other types.&lt;/p&gt;

&lt;p&gt;For the complete list of type precedence between all data types, check Microsoft docs on &lt;a href=&quot;https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-type-precedence-transact-sql?view=sql-server-ver15&quot;&gt;SQL Server data Type precedence&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;an-implicit-conversion-that-seeks&quot;&gt;An implicit conversion that seeks&lt;/h2&gt;

&lt;p&gt;In our example, the VARCHAR type was on the column, on the left side of the comparison in the WHERE. It means SQL Server had to read the whole content of the index to convert and then compare. That’s more than 2 million rows. That’s why the index scan.&lt;/p&gt;

&lt;p&gt;Let’s use the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Users&lt;/code&gt; table with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; as NVARCHAR and repeat the query. This time, switching the variable type to VARCHAR. What would be different?&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* We&apos;re filtering on the original Users table */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* This time, the column is NVARCHAR, but the parameter VARCHAR */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, the VARCHAR type is on the right of the comparison. It means SQL Server has to do one single conversion: the parameter.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-02-07-WhatAreImplicitConversions/IndexSeekOnLocation.png&quot; alt=&quot;Execution plan of finding all users StackOverflow users from Colombia&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Index Seek on Location index when finding all users from Colombia&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This time we don’t have a yellow bang on our execution plan. And, we have an Index Seek. Not all implicit conversions are bad.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In stored procedures and queries, use input parameters with the same types as the columns on the tables.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To identify which queries on your SQL Server have implicit conversions issues, we can use the third query from &lt;a href=&quot;/2020/09/28/SQLServerTuningTips/&quot;&gt;these six performance tuning tips from Pinal Dave&lt;/a&gt;. But, after taking &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar’s Mastering courses&lt;/a&gt;, I learned to start working with the most expensive queries instead of jumping to queries with implicit convertion issues right away.&lt;/p&gt;

&lt;p&gt;Voilà! Those are implicit conversions and why you should care. Let’s use input parameters with the right data types on your queries and store procedures. Otherwise, we will pay the performance penalty of converting and comparing types. Implicit conversions are like &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;functions around columns&lt;/a&gt;, implicitly added by SQL Server itself.&lt;/p&gt;

&lt;p&gt;Let’s remember that not all implicit conversions are bad. When looking at execution plans, let’s check how many rows SQL Server reads to convert and compare things.&lt;/p&gt;

&lt;p&gt;For more content on SQL Server, check &lt;a href=&quot;/2020/10/05/CompareDateTimeSQLServer/&quot;&gt;how to compare datetimes without the time part&lt;/a&gt;, &lt;a href=&quot;/2022/02/21/CaseSensitiveSearchSQLServer/&quot;&gt;how to write case-sensitive searches&lt;/a&gt; and &lt;a href=&quot;/2022/03/07/OptimizeGroupBySQLServer/&quot;&gt;how to optimize queries with GROUP BY&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Don&apos;t use functions around columns in your WHEREs: The worst T-SQL mistake</title>
   <link href="https://canro91.github.io/2022/01/24/DontPutFunctionsInYourWheres/"/>
   <updated>2022-01-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/01/24/DontPutFunctionsInYourWheres</id>
   <content type="html">&lt;p&gt;There’s one thing we could do to write faster queries in SQL Server: don’t use functions around columns in WHERE clauses. I learned it the hard way. Let me share this lesson with you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t use user-defined or built-in functions around columns in the WHERE clause of queries. It prevents SQL Server from estimating the right amount of rows out of the function. Write queries with operators and comparisons to make SQL Server better use the indexes it has.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;with-functions-around-columns-in-wheres&quot;&gt;With functions around columns in WHEREs&lt;/h2&gt;

&lt;p&gt;To prove this point, let’s query a local copy of the &lt;a href=&quot;https://www.brentozar.com/archive/2021/03/download-the-current-stack-overflow-database-for-free-2021-02/&quot;&gt;StackOverflow database&lt;/a&gt;. Yes, the StackOverflow we all know and use.&lt;/p&gt;

&lt;p&gt;StackOverflow has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Users&lt;/code&gt; table that contains, well…, all registered users and their profiles. Among other things, every user has a display name, location, and reputation.&lt;/p&gt;

&lt;p&gt;Let’s find the first 50 users by reputation in Colombia.&lt;/p&gt;

&lt;p&gt;To make things faster, let’s create an index on the Location field. It’s an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NVARCHAR(100)&lt;/code&gt; column.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the query we often write,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- Often, we put LOWER on both sides of the comparison&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LOWER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LOWER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--    ^^^^^             ^^^^^&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Did you notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOWER&lt;/code&gt; function on both sides of the equal sign?&lt;/p&gt;

&lt;p&gt;We all have written queries like that one. I declared myself guilty too. Often, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOWER&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPPER&lt;/code&gt; or wrap the column around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RTRIM&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LTRIM&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But, let’s see what happened in the Execution Plan.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-01-24-DontPutFunctionsInYourWheres/UsersInColombia.png&quot; alt=&quot;Execution plan of finding the first 50 StackOverflow users in Colombia&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;First 50 StackOverflow users in Colombia by reputation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here, SQL Server chose to scan the index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; first. And let’s notice the width of the arrow coming out of the first operator. When we place the cursor on it, it shows “Number of Rows Read.”&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-01-24-DontPutFunctionsInYourWheres/RowsRead.png&quot; alt=&quot;Rows read by Index Scan when finding StackOveflow users&quot; /&gt;
    &lt;figcaption&gt;Rows read by Index Scan on Location index&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In this copy of the StackOverflow database, there are 2,465,713 users, only 463 of them living in Colombia. SQL Server had to read the whole content of the index to execute our query. Arrrggg!&lt;/p&gt;

&lt;p&gt;It means that to find all users in Colombia, SQL Server had to go through all users in the index. We could use that index in a better way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write queries with comparisons, functions, and operators around parameters. This way SQL Server could properly use indexes and have better estimates of the contents of tables. But, don’t write functions around columns in the WHERE clauses.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same is true when joining tables. Let’s not put functions around the foreign keys in our JOINs either.&lt;/p&gt;

&lt;h2 id=&quot;rewrite-your-queries-to-avoid-functions-around-columns-in-where&quot;&gt;Rewrite your queries to avoid functions around columns in WHERE&lt;/h2&gt;

&lt;p&gt;Let’s go back and rewrite our query without any functions wrapping columns. This way,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Colombia&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- We remove LOWER on both sides&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reputation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And, let’s check the execution plan again.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-01-24-DontPutFunctionsInYourWheres/NoFunction.png&quot; alt=&quot;First 50 StackOverflow users in Colombia&quot; /&gt;
    &lt;figcaption&gt;Again, first 50 StackOverflow users in Colombia by reputation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This time, SQL Server used an Index Seek. It means SQL Server didn’t have to read the whole content of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; index to run our query. And the execution plan didn’t go parallel. We don’t have the black arrows in yellow circles in the operators.&lt;/p&gt;

&lt;p&gt;Let’s notice that this time we have a thinner arrow on the first operator. Let’s see how many rows SQL Server read this time.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2022-01-24-DontPutFunctionsInYourWheres/RowsReadWithoutFunction.png&quot; alt=&quot;Rows read by Index Seek on Location index&quot; /&gt;
    &lt;figcaption&gt;Rows read by Index Seek on Location index&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After that change, SQL Server only read 463 records. That was way better than reading the whole index.&lt;/p&gt;

&lt;p&gt;Voilà! If we want to write faster queries, let’s stop using functions around columns in our WHEREs. That screws SQL Server estimates. For example, let’s not use LOWER or UPPER around our columns. By the way, &lt;a href=&quot;/2022/02/21/CaseSensitiveSearchSQLServer/&quot;&gt;SQL Server string searches are case insensitive&lt;/a&gt; by default, we don’t need those functions at all.&lt;/p&gt;

&lt;p&gt;By the way, this is the same mistake we make when &lt;a href=&quot;/2020/10/05/CompareDateTimeSQLServer/&quot;&gt;comparing DateTime in our SQL queries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To read more SQL Server content, check &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;what implicit conversions are and why you should care&lt;/a&gt;, &lt;a href=&quot;/2022/03/07/OptimizeGroupBySQLServer/&quot;&gt;how to optimize queries with GROUP BY&lt;/a&gt;, and &lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;just listen to SQL Server index recommendations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy SQL time!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Better programming, flags, and C#</title>
   <link href="https://canro91.github.io/2022/01/17/MondayLinks/"/>
   <updated>2022-01-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/01/17/MondayLinks</id>
   <content type="html">&lt;p&gt;This episode of Monday Link is a bit diverse. From getting better at programming to why it’s harder for juniors to get hired. And, a rant about the C# evolution.&lt;/p&gt;

&lt;p&gt;I couldn’t avoid adding the last article to this Monday Links. I try to only share five articles. But, that’s a great story of establishing priorities and putting life, health, and work in a balance. Enjoy!&lt;/p&gt;

&lt;h2 id=&quot;a-path-to-better-programming-by-robert-uncle-bob-martin-and-allen-holub&quot;&gt;A Path to Better Programming by Robert “Uncle Bob” Martin and Allen Holub&lt;/h2&gt;

&lt;p&gt;This is a conversation between Uncle Bob and Allen Holub (I didn’t know about Allen before this conversation) about principles, isolation, mob programming, and leadership.&lt;/p&gt;

&lt;p&gt;By the way, the military has been leading large companies for years. How do they do it? The answer mentioned in the video is to empower people to do things. But there’s a whole book on the subject.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If it’s a good idea, the good idea tends to spread, unless there’s somebody at the management level working hard to make it not spread”&lt;/p&gt;

  &lt;p&gt;Allen Holub&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, I didn’t know there was a “Clean Agile” book. I would like to see what Agile meant for the creators and how it differs from what we call “Agile” today. &lt;a href=&quot;https://www.youtube.com/watch?v=QnmRpHFoYLk&quot;&gt;Watch full video&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-flags-do-not-represent-languages&quot;&gt;Why flags do not represent languages&lt;/h2&gt;

&lt;p&gt;These days I needed to translate my &lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;Unit Testing 101 workshop&lt;/a&gt; instructions to Spanish. And call it fate or not, I found this article about not using flags to represent languages and what to do instead. How often do you see on web pages the US flag to signal English? &lt;a href=&quot;http://www.flagsarenotlanguages.com/blog/why-flags-do-not-represent-language/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1580117287510-ad5925c3c074?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzMzYxNzcxNg&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;President and Mrs. Coolidge attended Thanksgiving Day service&quot; /&gt;

&lt;figcaption&gt;There were days without daily meetings. Photo by &lt;a href=&quot;https://unsplash.com/@libraryofcongress?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Library of Congress&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;is-c-getting-too-complex&quot;&gt;Is C# Getting Too Complex?&lt;/h2&gt;

&lt;p&gt;I like the evolution of C# as a language. I wrote about some of &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;the newest and coolest C# features&lt;/a&gt;. But, I don’t like some of the new features. Some features, quoting the article, &lt;em&gt;“just add to the complexity of the language without bringing anything to the table besides saving a few lines of code or a couple of curly braces.”&lt;/em&gt; Some features make the language less consistent. I share the main point of this article. &lt;a href=&quot;https://medium.com/nerd-for-tech/is-c-getting-too-complicated-for-its-own-good-83c149a6faca&quot;&gt;Read full article&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;read-the-source-code-of-your-dependencies&quot;&gt;Read the source code of your dependencies&lt;/h2&gt;

&lt;p&gt;I first heard about the concept of reading code as a learning exercise from a friend. To learn how star developers code, read their code. This post takes this concept to the next level. Use only dependencies you’re able to read its source code. &lt;a href=&quot;https://changelog.com/posts/read-the-source-code-of-your-dependencies&quot;&gt;Read full article&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;why-nobody-hires-junior-developers-and-what-happens-next&quot;&gt;Why nobody hires junior developers and what happens next&lt;/h2&gt;

&lt;p&gt;Another consequence of the pandemic. Nobody hires juniors. One thing I’ve noticed is companies don’t have clear career plans and salary policies for developers. Someone starts in a company and after a few years, what? Close tickets forever? Work extra hours to get noticed and promoted? That’s why juniors as soon as they feel more confident, move to a better-paid place. Brain drain is expensive. &lt;a href=&quot;https://www.notonlycode.org/nobody-hires-juniors/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;fetch-the-bolt-cutters&quot;&gt;Fetch the bolt cutters&lt;/h2&gt;

&lt;p&gt;Maybe I don’t have serious eye strain or carpal tunnel syndrome (yet?). But, I can totally relate to this story. And, it’s something more frequent these days. One quote that made me wow and stand in ovation: &lt;em&gt;“In a choice between a job and me, I’m always going to choose me”.&lt;/em&gt; Such an amazing way to finish that story. &lt;a href=&quot;https://thewebivore.com/fetch-the-bolt-cutters/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Another &lt;del&gt;five&lt;/del&gt; six reads! Do you also think C# is getting too complex? Have you seen a clear career path at companies where you’ve been?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. &lt;a href=&quot;https://fridaylinks.beehiiv.com/subscribe&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Subscribe to my Friday Links here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy reading!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Best of 2021</title>
   <link href="https://canro91.github.io/2022/01/10/BestOf2021/"/>
   <updated>2022-01-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2022/01/10/BestOf2021</id>
   <content type="html">&lt;p&gt;In 2021, I switched from posting whenever I had an idea about anything to posting regularly every other week about some topics.&lt;/p&gt;

&lt;p&gt;I wrote a whole series of posts about unit testing. From how to write your first unit test with MSTest to what stubs and mocks are. In fact, I wrote my first ebook &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; with some of the posts of the series. By the way, you can download it for free. No email asked.&lt;/p&gt;

&lt;p&gt;These are the 5 posts I wrote in 2021 you read the most. In case you missed any of them, here they are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;How to name your unit tests. 4 test naming conventions&lt;/a&gt;. This post is part of the Unit Testing 101 series. Four ideas to better name our unit tests. All of them, to write names easy to understand.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;What are fakes in unit testing: mocks vs stubs&lt;/a&gt;. Again, part of Unit Testing 101. This post shows what fakes, stubs, and mocks are. It might sound difficult, but it isn’t.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/02/24/ParseTwoDigitYear/&quot;&gt;TIL: How to convert 2-digit year to 4-digit year in C#&lt;/a&gt;. Last year, I worked with a Stripe-powered payment module in a reservation management system. And, I needed to parse card expiration dates from a 2-digit year to a 4-digit year. I almost just added 2000 to them. But I didn’t. This is what I found and did instead.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/02/10/DecoratorPattern/&quot;&gt;Decorator pattern. A real example in C#&lt;/a&gt;. My favorite pattern. This post shows how to implement a retry mechanism on top of Stripe API client.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;A quick guide to LINQ with examples&lt;/a&gt;. I wrote this post to answer a friend. All you need to know to start working with LINQ in 15 minutes or less.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voilà! These were your 5 favorite posts. Hope you enjoy them. Probably, you found digests of these posts on my &lt;a href=&quot;https://dev.to/canro91&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dev.to&lt;/a&gt; account too.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading, and happy coding in 2022!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Art of Readable Code: Takeaways</title>
   <link href="https://canro91.github.io/2021/12/20/TheArtOfReadableCodeReview/"/>
   <updated>2021-12-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/12/20/TheArtOfReadableCodeReview</id>
   <content type="html">&lt;p&gt;The Art of Readable Code is the perfect companion for the &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt;. It contains simple and practical tips to improve your code at the function level. It isn’t as dogmatic as Clean Code. Tips aren’t as strict as the ones from Clean Code. But, it still deserves to be read.&lt;/p&gt;

&lt;p&gt;These are some of my notes on The Art of Readable Code.&lt;/p&gt;

&lt;h2 id=&quot;1-code-should-be-easy-to-understand&quot;&gt;1. Code should be easy to understand&lt;/h2&gt;

&lt;p&gt;Code should be written to minimize the time for someone else to understand it. Here, understanding means solving errors, spotting bugs, and making changes.&lt;/p&gt;

&lt;p&gt;It’s good to write compact code. But, compact doesn’t always mean more readable.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FindBucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsOccupied&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// vs&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FindBucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsOccupied&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-surface-level-improvements&quot;&gt;2. Surface-level improvements&lt;/h2&gt;

&lt;h3 id=&quot;packing-info-into-names&quot;&gt;Packing info into names&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;If something is critical to understand, put it in a name.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choose specific words for your method names. For example, does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def GetPage(url)&lt;/code&gt; get the page from the network, a cache or a database? Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FetchPage(url)&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DownloadPage(url)&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Avoid empty names like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retval&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;. Instead, use a variable name that describes the value. In the next code sample, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum_squares&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retval&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;norm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;retval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;retval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// sum_squares = v[i] * v[i];&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;retval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Variables &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;j&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k&lt;/code&gt; don’t always work for loop indices. Prefer more concrete names. Indices could be misused or interchanged.&lt;/p&gt;

&lt;p&gt;Use concrete over abstract names. Instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--run-locally&lt;/code&gt;, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--extra-logging&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--use-local-database&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Attach extra information to your names. For example, encode units. Prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delay_secs&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delay&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size_mb&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;size&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;degrees&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;angle&lt;/code&gt;. Also, encode extra information. Prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plaintext_password&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Do not include needless words in your names. Instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConvertToString&lt;/code&gt;, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToString&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1605402756180-75934835ce13?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzNjM4ODA1Mg&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;&apos;Your Name Here&apos; Sign on the Side of a Building&quot; /&gt;

&lt;figcaption&gt;What name would you put in that sign? Photo by &lt;a href=&quot;https://unsplash.com/@austinkirk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Austin Kirk&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/name?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;names-cant-be-misconstructed&quot;&gt;Names can’t be misconstructed&lt;/h3&gt;

&lt;p&gt;Make your names resistant to misinterpretation. Does a method on arrays named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; pick or get rid of elements? If it picks elements, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select()&lt;/code&gt;. And, if it gets rid of elements, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exclude()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Does results have elements that satisfy the condition? Or elements that don&apos;t?&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;all_objects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;year &amp;lt;= 2011&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use Min and Max for inclusive limits. Put min or max in front of the thing being limited. For example,&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shopping_cart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_ITEMS_IN_CART&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use First and Last for inclusive limits. What’s the result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print integer_range(start=2, stop=4)&lt;/code&gt;? Is it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[2,3]&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[2,3,4]&lt;/code&gt;? Prefer First and Last. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print integer_range(first=2, last=4)&lt;/code&gt; to mean &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[2,3,4]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use Begin and End for inclusive/exclusive ranges. For example, to find all events on a date, prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PrintEventsInRange(&quot;OCT 16 12:00am&quot;, &quot;OCT 17 12:00am&quot;)&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PrintEventsInRange(&quot;OCT 16 12:00am&quot;, &quot;OCT 16 11:59.999am&quot;)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For booleans variables, make clear what true or false means. Use is, has, can, should, need as prefixes. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SpaceLeft()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasSpaceLeft()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Avoid negated booleans. For example, instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable_ssl = false&lt;/code&gt;, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use_ssl = true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Don’t create false expectation. With these two names &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetSize()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ComputeSize()&lt;/code&gt;, we expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetSize()&lt;/code&gt; to be a lightweight operation.&lt;/p&gt;

&lt;h3 id=&quot;aesthetics&quot;&gt;Aesthetics&lt;/h3&gt;

&lt;p&gt;Similar code should look similar. Pick a meaningful order and maintain it. If the code mentions A, B, and C, don’t say B, C, and A in other places.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;details&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;details&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;location&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;phone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;phone&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;phone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;phone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;phone&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;details&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;details&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Organize declarations into blocks, like sentences. Break code into paragraphs.&lt;/p&gt;

&lt;h3 id=&quot;knowing-what-to-comment&quot;&gt;Knowing what to comment&lt;/h3&gt;

&lt;p&gt;Don’t comment what can be derived from code. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GOOD CODE &amp;gt; BAD CODE + COMMENTS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Comment the why’s behind a decision. Anticipate likely questions and comment the big picture. Comment why you choose a particular value or what it’s a valid range. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAX_THREADS = 8; // Up to 2*num of procs&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;making-comments-precise-and-compact&quot;&gt;Making comments precise and compact&lt;/h3&gt;

&lt;p&gt;Use comments to show examples of input and output values.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Strip(&quot;ab&quot;, &quot;a&quot;) == &quot;b&quot;&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Strip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// CategoryType -&amp;gt; (score, weight)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;typdef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash_map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use named parameters or use comments to make the same effect. Prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connect(timeout: 10, use_ssl: false)&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connect(10, false)&lt;/code&gt;. In languages without named parameters, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connect(/*timeout_ms=*/10, /*use_ssl=*/false)&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-simplifying-loops-and-logic&quot;&gt;3. Simplifying loops and logic&lt;/h2&gt;

&lt;h3 id=&quot;making-control-flow-easy-to-read&quot;&gt;Making control flow easy to read&lt;/h3&gt;

&lt;p&gt;When doing conditionals,  write the changing variable first, followed by an operator and by stable expression. Prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (length &amp;gt;= 10)&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (10 &amp;lt;= length)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When doing if-then-else, treat the positive case first or the simplest case or the most interesting case first.&lt;/p&gt;

&lt;p&gt;Use the ternary operator &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?:&lt;/code&gt; with simple statements, not to squeeze logic into a single line. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time_str += (hour &amp;gt; 12) ? &quot;pm&quot; : &quot;am&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Avoid do-while loops. Do-while loops break the convention of keeping the condition first. Use while instead.&lt;/p&gt;

&lt;h3 id=&quot;breaking-down-giant-expressions&quot;&gt;Breaking down giant expressions&lt;/h3&gt;

&lt;p&gt;Write explaining variables and summary variables. For example,&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;:&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# vs
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;:&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use De Morgan’s Laws. For example,&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_exists&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_protected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# vs
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_exists&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_protected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Eliminate intermediate results. &lt;strong&gt;Do your task as quickly as possible&lt;/strong&gt;. For example, avoid writing functions like this&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;remove_one&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// find index&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// remove&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1627873828946-44e8b5261d2d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzNjU2NTM5OA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Bunch of pencils organized by color&quot; /&gt;

&lt;figcaption&gt;Let&apos;s keep our code organize, shall we? Photo by &lt;a href=&quot;https://unsplash.com/@lucasgwendt?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Lucas George Wendt&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/organized?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;4-reorganizing-your-code&quot;&gt;4. Reorganizing your code&lt;/h2&gt;

&lt;h3 id=&quot;extracting-unrelated-problems&quot;&gt;Extracting unrelated problems&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Separate the generic code from the specific code&lt;/strong&gt;. Extract pure utility code into libraries or a set of functions.&lt;/p&gt;

&lt;p&gt;Simplify existing interfaces. You should never have to settle for an interface that’s less than ideal.&lt;/p&gt;

&lt;p&gt;To separate generic code from specific code, ask yourself:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What is the high-level goal of this code?&lt;/li&gt;
  &lt;li&gt;Is every line working to that goal? Or is it solving an unrelated problem?&lt;/li&gt;
  &lt;li&gt;If enough lines are working on an unrelated problem, extract that code into a separated function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, this code doesn’t separate generic from specific code&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;on_sucess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// format_pretty&lt;/span&gt;
		&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;nx&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format_pretty&lt;/code&gt; takes care of generic code.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;na&quot;&gt;on_sucess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;nx&quot;&gt;format_pretty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;one-thing-at-a-time&quot;&gt;One thing at a time&lt;/h3&gt;

&lt;p&gt;List all the things your code is doing. And try to separate every task into a separate function.&lt;/p&gt;

&lt;p&gt;Defragment your code to do one type of thing at a time. For example, initialize to default values, then calculate some data and lastly update some other values.&lt;/p&gt;

&lt;h3 id=&quot;writing-less-code&quot;&gt;Writing less code&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The most readable code is no code at all&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Keep your codebase as small and lightweight as possible. Create as much utility code to remove duplication. Remove unused code. Keep your project separated into isolated sub-projects&lt;/p&gt;

&lt;p&gt;Know the capabilities of your libraries. Every once in a while spend 15 minutes reading the names of all functions/modules/types in your standard libraries. &lt;strong&gt;Write less code as possible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, don’t be tempted to write your own de-duplicate code in Python.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	
&lt;span class=&quot;n&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# vs
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unique&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-testing-and-readability&quot;&gt;5. Testing and readability&lt;/h2&gt;

&lt;p&gt;Tests should be easy to understand. Coders are afraid of changing code, so coders don’t add new tests.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hide less important details from the user, so he can focus only on the important ones.&lt;/li&gt;
  &lt;li&gt;Create the minimal test statement. Most tests can be reduced to: “given an input, expect this output.” &lt;strong&gt;Ideally a unit test is just 3-line long&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Create mini-languages.&lt;/li&gt;
  &lt;li&gt;Choose the simplest set of inputs that exercise your code. Prefer clean and simple test values. &lt;strong&gt;Embrace the Least astonishing principle&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Write smaller test cases, instead of a single perfect one-size-fits-all test case. Every test pushes your code in a different direction.&lt;/li&gt;
  &lt;li&gt;Test names should indicate a unit of work (or code under test), situation or bug being tested, and expected result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voilà! These are some of my notes. One tip I started to practice after reading this book was to “extract unrelated problems.”&lt;/p&gt;

&lt;p&gt;The Art of Readable Code is a good starting point to introduce the concept of readability and clean code to your team. These tips and tricks are a good reference for code standards and reviews.&lt;/p&gt;

&lt;p&gt;If you’re interested in other books, take a look at my takeaways from &lt;a href=&quot;/2020/06/15/CleanCoder/&quot;&gt;Clean Coder&lt;/a&gt;, &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Clean Code&lt;/a&gt; and &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy reading!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Domain Modeling Made Functional: Takeaways</title>
   <link href="https://canro91.github.io/2021/12/13/DomainModelingMadeFunctional/"/>
   <updated>2021-12-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/12/13/DomainModelingMadeFunctional</id>
   <content type="html">&lt;p&gt;If you are curious about the functional world, but you found it too hard to start, this book is a good place to start. These are my notes and takeaways from “Domain Modeling Made Functional” by Scott Wlaschin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Domain modeling made functional” teaches to start coding only after understanding the domain. And to capture requirements, constraints, and business rules using types in the system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This book covers from Domain-Driven Design (DDD) to type systems to refining an existing domain model. It’s a jump to the functional world by designing a system to price and ship orders in a supply store. No jargon or weird concepts needed.&lt;/p&gt;

&lt;p&gt;All the code samples are in F#. Most of the time, they’re easy to understand. Since F# has a better type inference than C#, types aren’t explicit all the time while writing functions. Some code listings are hard to translate to C#.&lt;/p&gt;

&lt;p&gt;To follow the code using C#, I had to rely upon libraries like &lt;a href=&quot;https://github.com/mcintyre321/OneOf&quot;&gt;OneOf&lt;/a&gt; and &lt;a href=&quot;https://github.com/nlkl/Optional&quot;&gt;Optional&lt;/a&gt; to bring &lt;a href=&quot;/2024/08/19/DiscriminatedUnionSupport/&quot;&gt;discriminated unions&lt;/a&gt; and option types, not built into the C# language yet.&lt;/p&gt;

&lt;h2 id=&quot;1-ddd-and-ubiquitous-language&quot;&gt;1. DDD and Ubiquitous language&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The goal of DDD is to share the business model between developers and domain experts&lt;/strong&gt;. In DDD, the business domain drives the design, not the database schema. Don’t rush to think in terms of database tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write code using the same vocabulary from the business domain&lt;/strong&gt;. Domain experts or product owners don’t think in integers or strings. But, in business concepts like OrderId, ProductCode. When in doubt, ask your domain expert what an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderBase&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderHelper&lt;/code&gt; mean.&lt;/p&gt;

&lt;h2 id=&quot;2-types-everywhere&quot;&gt;2. Types everywhere&lt;/h2&gt;

&lt;p&gt;A type is a name for all possible input and output values in a function. For example, Math functions from one set to another.&lt;/p&gt;

&lt;p&gt;There are two ways to construct types: AND types and OR types. An AND type is a combination of other types. And an OR type is a choice between a known set of types. OR types are also called discriminated unions or choice types.&lt;/p&gt;

&lt;p&gt;For example, a meal is a combination of an entrée, a main course, and a dessert. And, a dessert is either a cake or a flan. To represent a meal, we need an AND type. And for a dessert, an OR type.&lt;/p&gt;

&lt;p&gt;These would be the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Meal&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dessert&lt;/code&gt; types in F#,&lt;/p&gt;

&lt;div class=&quot;language-fsharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Meal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Entree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EntreeInfo&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;MainCourse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainCourseInfo&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Dessert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DessertInfo&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DessertInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Cake&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Flan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the Object-Oriented (OO) world, types are like classes without behavior or single-method interfaces. Types represent a set of functions too. AND types are regular classes with properties. While OR types are enums with, possibly, members of different types.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1631856954913-c751a44490ec?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzNDE2NzI1Mw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;plumbing supplies in Home Depot&quot; /&gt;

&lt;figcaption&gt;Who needs plumbing supplies? Photo by &lt;a href=&quot;https://unsplash.com/@oksdesign?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Oxana Melis&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/hardware-store?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-restrictions-errors-and-invalid-state&quot;&gt;3. Restrictions, Errors, and Invalid State&lt;/h2&gt;

&lt;h3 id=&quot;restrictions&quot;&gt;Restrictions&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Express restrictions in your design and enforce constraints with new types&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, to represent unit quantities in orders, don’t use a plain integer. “Unit” is a concept in the business domain. It should be in a separate type, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnitQuantity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To restrict unit quantities between 1 and 1000, create a private constructor in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnitQuantity&lt;/code&gt; type and only expose a factory method with the validation.&lt;/p&gt;

&lt;p&gt;To enforce that an order should have at least one line item, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NonEmptyList&lt;/code&gt; instead of a possibly empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To represent optional values, &lt;a href=&quot;/2023/03/20/UseOptionInsteadOfNull/&quot;&gt;use Option&amp;lt;T&amp;gt; instead of null&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;errors-and-exceptions-signature-method-honesty&quot;&gt;Errors and Exceptions: Signature method honesty&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Make errors part of your domain&lt;/strong&gt;. Follow the Signature Method honesty. This means documenting all possible outputs in the signature of methods.&lt;/p&gt;

&lt;p&gt;For example, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Divide&lt;/code&gt; function shouldn’t throw an exception. Instead write, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int Divide(int a, NonZeroInt b);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Stay away from exceptions and instead use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt; type to wrap failed and successful types.&lt;/p&gt;

&lt;p&gt;For example, to check addresses return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&amp;lt;CheckedAddress, AddressValidationError&amp;gt;&lt;/code&gt;. It means either a valid address or a validation error.&lt;/p&gt;

&lt;div class=&quot;language-fsharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CheckAddressExists&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UnvalidatedAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CheckedAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AddressValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AddressValidationError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; 
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;InvalidFormat&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AddressNotFound&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To work with exceptions in third-party code, wrap that code in a function that catches exceptions and returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&lt;/code&gt;. Don’t catch all exceptions, only those relevant to the domain. Like, timeouts or failed logins.&lt;/p&gt;

&lt;h3 id=&quot;make-illegal-state-unrepresentable&quot;&gt;Make illegal state unrepresentable&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Convert a design with two choices to a design with one type per choice&lt;/strong&gt;. Booleans are generally a bad design choice.&lt;/p&gt;

&lt;p&gt;To represent validated emails, don’t create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomerEmail&lt;/code&gt; with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsVerified&lt;/code&gt; flag. Instead, create separate types, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifiedEmailAddress&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnverifiedEmailAddress&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With types, we don’t need unit tests for invalid situations, we have compile-time unit tests. Also, types better document the business domain.&lt;/p&gt;

&lt;h2 id=&quot;4-transformation-oriented-programming&quot;&gt;4. Transformation-oriented programming&lt;/h2&gt;

&lt;p&gt;Write actions in your system as workflows or chains of transformations.&lt;/p&gt;

&lt;p&gt;A workflow is a pipeline where the output type of every step is the input of the next one. A workflow receives commands and returns events as outputs. A command should have everything needed to start a workflow.&lt;/p&gt;

&lt;p&gt;Put all the I/O at the edges. Call the database at the beginning or at the end of a workflow.&lt;/p&gt;

&lt;p&gt;For example, these are the types of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PlaceOrderWorkflow&lt;/code&gt; and the types of its steps.&lt;/p&gt;

&lt;div class=&quot;language-fsharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValidateOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;nc&quot;&gt;UnvalidatedOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValidatedOrder&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PriceOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValidatedOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PricedOrder&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AcknowledgeOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PricedOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AcknowlegementSent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateEvents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PricedOrder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlaceOrderEvent&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;list&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlaceOrderWorflow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlaceOrderCommand&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;PlaceOrderEvent&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PlaceOrderError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-challenges&quot;&gt;5. Challenges&lt;/h2&gt;

&lt;p&gt;After reading this book, there are two things I want to change in the codebases around me.&lt;/p&gt;

&lt;h3 id=&quot;1-replace-constants-class-with-enums&quot;&gt;1. Replace Constants class with enums&lt;/h3&gt;

&lt;p&gt;First, don’t use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constants&lt;/code&gt; class for an exhaustive list of known values or options. Use enums instead.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Constants&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Authorize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Authorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Capture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Refund&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refund&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToLower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentResponse&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PerformOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By using strings as parameters, we can pass any string to the receiving method. But, the only valid options are then ones inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Constants&lt;/code&gt; class. Enums are better to make illegal states unrepresentable in this case.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OperationType&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Authorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Refund&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentResponse&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PerformOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OperationType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-separate-states-separate-classes&quot;&gt;2. Separate states, separate classes&lt;/h3&gt;

&lt;p&gt;Last challenge, don’t use a single class with a boolean property to represent two states. Use two types instead.&lt;/p&gt;

&lt;p&gt;For example, don’t write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentResponse&lt;/code&gt; with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasError&lt;/code&gt; flag for failed and successful payments. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentResponse&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HasError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrorMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead, write two separate classes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FailedPayment&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SuccessfulPayment&lt;/code&gt;. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FailedPayment&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HasError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrorMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SuccessfulPayment&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// No HasError and ErrorMessage...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, as part of this last challenge, not only separate a class with a boolean type into two classes, but also &lt;a href=&quot;/2023/04/03/SeparateStateIntoSeparateObjects/&quot;&gt;separate optional or nullable state to avoid NullReferenceException&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;6-putting-types-into-practice&quot;&gt;6. Putting types into practice&lt;/h2&gt;

&lt;p&gt;I had the chance to practice what I’ve learned with this book, even before finishing it.&lt;/p&gt;

&lt;p&gt;Here are my requirements. After getting a reservation, hotels want to charge a deposit before the guests arrive. They want to charge some nights, a fixed amount, or a percentage, either after getting the reservation or before the arrival date.&lt;/p&gt;

&lt;p&gt;This is what I came up with. Better than a single class full of string, bool, and int properties. That would have been my approach before reading this book.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Deposit&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Charge&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Night&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Nights&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FixedAmount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Currency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;USD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Euro&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MXN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;COP&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Percentage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Charge&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Delay&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Days&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DelayType&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BeforeCheckin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AfterReservation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;7-parting-thoughts&quot;&gt;7. Parting thoughts&lt;/h2&gt;

&lt;p&gt;Voilà! These are the lessons I learned from this book. It’s a good introduction to DDD and functional programming. If you’re not a functional programmer, you can still take advantage of the concepts of this book in your everyday programming. I did.&lt;/p&gt;

&lt;p&gt;You can skip the last 2 or 3 chapters. If you’re new to F# and want to work with it, they contain good examples of serialization and databases. But, if you want to adopt these concepts to your OO world, you can skim these chapters for the main concepts.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“A class with a boolean to represent two states is generally a smell.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I liked that one.&lt;/p&gt;

&lt;p&gt;For more takeaways, check &lt;a href=&quot;/2020/06/15/CleanCoder/&quot;&gt;Clean Coder&lt;/a&gt; and &lt;a href=&quot;/2020/03/06/TheArtOfUnitTestingReview/&quot;&gt;The Art of Unit Testing&lt;/a&gt;. Don’t miss, &lt;a href=&quot;/2020/12/10/PrimitiveObsession/&quot;&gt;A case of Primitive Obsession&lt;/a&gt;, it shows how to put in place classes to replace plain primitive types.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>It&apos;s not what you read, it&apos;s what you ignore</title>
   <link href="https://canro91.github.io/2021/12/06/ItsNotWhatYouRead/"/>
   <updated>2021-12-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/12/06/ItsNotWhatYouRead</id>
   <content type="html">&lt;p&gt;A long time ago, I watched an online presentation by Scott Hanselman (&lt;a href=&quot;https://twitter.com/shanselman&quot;&gt;@shanselman&lt;/a&gt;) about productivity. One of the best I’ve seen. These days I found my handwritten notes about it. Today, I want to share my takeaways and the tools I’ve used since I watched it the first time.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/IWPgUn8tL8s?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;triage-every-inbox&quot;&gt;Triage every inbox&lt;/h2&gt;

&lt;p&gt;Do triage the same way doctors do it. &lt;strong&gt;Set three goals for your day, week, and year&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Separate your tasks into four categories divided into two quadrants: important versus urgent.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If something is Urgent and Important, act on it immediately. Think of a server down.&lt;/li&gt;
  &lt;li&gt;If something is Important but Not Urgent, it’s your regular job. Work on it.&lt;/li&gt;
  &lt;li&gt;If something is Not Urgent and Not Important, delegate it. Ask somebody else to do it.&lt;/li&gt;
  &lt;li&gt;If something is Not Urgent and Not Important, drop it. Ignore it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;dont-read-email-first-thing-in-the-morning&quot;&gt;Don’t read email first thing in the morning&lt;/h2&gt;

&lt;p&gt;Create rules in your email. Separate your emails into folders. For example,&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Inbox&lt;/strong&gt; for regular emails&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;CC&lt;/strong&gt; for emails where you’re CCed. These are FYI emails&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Bosses&lt;/strong&gt; for your managers, CTO and CEO&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;External&lt;/strong&gt; for emails from customers and clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conserve your keystrokes. Imagine your number of keystrokes is limited. Write 3 or 5 sentence emails. Anything longer should be in a wiki, blog post, or documentation.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1513544705284-99373737fab6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzMDQ1OTk5NQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;timer on a table&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@marceloleal80?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Marcelo Leal&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/pomodoro-timer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;use-pomodoro-technique&quot;&gt;Use Pomodoro technique&lt;/h2&gt;

&lt;p&gt;Work in 25-minute sessions of intense concentration between 5-minute breaks. After 4 or 5 work sessions, take a longer break for 15 minutes. This is the Pomodoro technique in a nutshell.&lt;/p&gt;

&lt;p&gt;During your Pomodoros, keep track of your internal and external interruptions. For example, I keep my cell phone out of sight and in silence mode while working on something important.&lt;/p&gt;

&lt;h2 id=&quot;dont-set-up-a-guilty-system&quot;&gt;Don’t set up a guilty system&lt;/h2&gt;

&lt;p&gt;Put on your desktop what you really are going to do. Don’t pile up books on your desktop.&lt;/p&gt;

&lt;p&gt;When you feel overwhelmed, sync to paper. Write down what you have to do.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“If it’s not helping me to &amp;lt;put-your-own-goal-here&amp;gt;, if it’s not improving my life in some way, it’s mental clutter and it’s out”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Voilà! These are some of my takeaways. I learned from this presentation to keep my emails separated into folders and to consider my keystrokes limited. For example, I’ve written some of my posts to answer friends and coworkers. That way I can help more than one person while keeping my keystrokes limited.&lt;/p&gt;

&lt;p&gt;For more content on productivity, check my &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;Visual Studio setup for C#&lt;/a&gt; and some &lt;a href=&quot;/2020/04/13/ProgramThatSave100Hours/&quot;&gt;tools that saved me 100 hours&lt;/a&gt;. For other presentations, check &lt;a href=&quot;/2021/01/25/LivableCode/&quot;&gt;Livable Code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Want to ace your next take-home coding exercises? Follow these 13 short tips</title>
   <link href="https://canro91.github.io/2021/11/22/CodingChallengeTips/"/>
   <updated>2021-11-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/11/22/CodingChallengeTips</id>
   <content type="html">&lt;p&gt;Take-home coding exercises aren’t rare in interviews.&lt;/p&gt;

&lt;p&gt;I’ve found from 4-hour exercises to 2-day exercises to a Pull Request in an open-source project.&lt;/p&gt;

&lt;p&gt;Even though, in my CV I have links to my GitHub profile (with some green squares, not just cricket sounds) and my personal blog (the one you’re reading), I’ve had to finish take-home exercises.&lt;/p&gt;

&lt;p&gt;Yes, hiring is broken!&lt;/p&gt;

&lt;p&gt;For my last job, I had to solve a take-home challenge when applying as a C#/.NET backend software engineer. It was a decent company and I wanted to play the hiring game. So I accepted.&lt;/p&gt;

&lt;p&gt;After I joined I was assigned to review applicants’ solutions.&lt;/p&gt;

&lt;p&gt;Here are 13 short tips to help you solve your next take-home interview exercise. These are the same guidelines I used to review other people’s take-home exercises.&lt;/p&gt;

&lt;h2 id=&quot;stick-to-standards&quot;&gt;Stick to standards&lt;/h2&gt;

&lt;p&gt;Follow good practices and stick to coding standards. &lt;a href=&quot;/2020/01/06/CleanCodeReview/&quot;&gt;Write clean code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Write descriptive names&lt;/strong&gt;. Don’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Information&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Info&lt;/code&gt; as name suffixes, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountInfo&lt;/code&gt;. Keep names consistent all over your code. If you used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddAccount&lt;/code&gt; in one place, don’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountAdd&lt;/code&gt; anywhere else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use one single “spoken” language&lt;/strong&gt;. Write variables, functions, classes, and all other names in the same language. Don’t mix English and your native language if your native language isn’t English. Always prefer the language you will be using at the new place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Don’t keep commented-out code&lt;/strong&gt;. And don’t have extra blank lines. &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;Use linters and extensions&lt;/a&gt; to keep your code tidy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Use third-party libraries to solve common problems&lt;/strong&gt;. But don’t keep unused libraries or NuGet packages around.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Have clearly separated responsibilities&lt;/strong&gt;. Use separate classes, maybe Services and Repositories or Commands and Queries. But stay away from &lt;a href=&quot;/2022/12/07/BanningSomeNamingConventions/&quot;&gt;Helper and Utility classes full of static methods&lt;/a&gt;. Often, they mean you have wrong abstractions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Add unit or integration tests&lt;/strong&gt;, at least for the main parts of your solution.&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing or want to learn more, don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover everything from the basics of unit testing to mocking and best practices.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1512902990232-3ff067da0597?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzMDAzNzQ0OQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Framed wall art&quot; /&gt;

&lt;figcaption&gt;Write code you would print and hang on a wall. Photo by &lt;a href=&quot;https://unsplash.com/@lefty_kasdaglis?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Lefty Kasdaglis&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/picture-wall?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;version-control-your-solution&quot;&gt;Version Control your solution&lt;/h2&gt;

&lt;p&gt;Keep your solution under version control. &lt;a href=&quot;/2020/05/29/HowToVersionControl/&quot;&gt;Use Git&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Write small and incremental commits.&lt;/strong&gt; Don’t just use a single commit with “Finished homework” as the message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Write good commit messages&lt;/strong&gt;. Don’t use “Uploading changes,” “more changes,” or anything along those lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Use GitHub, GitLab, or any hosting service,&lt;/strong&gt; unless you have different instructions for your take-home exercise.&lt;/p&gt;

&lt;h2 id=&quot;write-a-good-readme-file&quot;&gt;Write a good README file&lt;/h2&gt;

&lt;p&gt;Write a good README file for your solution. Don’t miss this one! Seriously!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Add installation instructions&lt;/strong&gt;. Make it easy for reviewers to install and run your solution. Consider using Docker or deploying your solution to a free hosting provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11. Add instructions to run and test your solution&lt;/strong&gt;. Maybe, some step-by-step instructions with screenshots or a Postman collection with the endpoints to hit. You get the idea!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12. Tell what third-party tools you used&lt;/strong&gt;. Include what libraries, NuGet packages, or third-party APIs you used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. Document any major design choices&lt;/strong&gt;. Did you choose any architectural patterns? Any storage layer? Tell why.&lt;/p&gt;

&lt;p&gt;Voilà! These are my best tips to succeed at your next take-home interview challenge. Remember, it’s your time to shine. Write code as clean as possible and maintain consistency. Good luck!&lt;/p&gt;

&lt;p&gt;If you want to see how I followed these tips on a real take-home coding exercise, check &lt;a href=&quot;https://github.com/canro91/SignalChat&quot;&gt;SignalChat&lt;/a&gt; on my GitHub account. It’s a simple browser-based chat application using ASP.NET Core and SignalR.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/SignalChat&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/SignalChat.svg&quot; alt=&quot;canro91/SignalChat - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get ready for your next interview with these &lt;a href=&quot;/2019/09/29/RemoteInterviewTips/&quot;&gt;tips for remote interviews&lt;/a&gt;. And, to prepare for your technical interviews, check &lt;a href=&quot;/2019/08/02/PostfixNotationAnInterviewExercise/&quot;&gt;how to evaluate a postfix expression&lt;/a&gt;, &lt;a href=&quot;/2019/08/29/TimeComplexity/&quot;&gt;how to solve the two-sum problem&lt;/a&gt;, and &lt;a href=&quot;/2019/09/16/RotatingArray/&quot;&gt;how to shift array elements&lt;/a&gt;. These are real questions I got in previous interviews.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>BugOfTheDay: There are pending requests working on this transaction</title>
   <link href="https://canro91.github.io/2021/11/08/PendingRequestInTransaction/"/>
   <updated>2021-11-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/11/08/PendingRequestInTransaction</id>
   <content type="html">&lt;p&gt;These days I got this exception message: “The transaction operation cannot be performed because there are pending requests working on this transaction.” This is how I fixed it after almost a whole day of Googling and debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To fix the “pending requests” exception, make sure to properly await all asynchronous methods wrapped inside any database transaction.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;pipeline-pattern-and-reverting-reservations&quot;&gt;Pipeline pattern and reverting reservations&lt;/h2&gt;

&lt;p&gt;I was working with &lt;a href=&quot;/2020/02/14/PipelinePattern/&quot;&gt;the pipeline pattern&lt;/a&gt; to book online reservations.&lt;/p&gt;

&lt;p&gt;The reservation process used two types of steps inside a pipeline: foreground and background steps.&lt;/p&gt;

&lt;p&gt;The foreground steps ran to separate enough rooms for the reservation. And the background steps did everything else to fulfill the reservation but in background jobs.&lt;/p&gt;

&lt;p&gt;If anything wrong happened while executing the foreground steps, the whole operation rollbacked. And there were no rooms set aside for the incoming reservation. To achieve this, every foreground step had a method to revert its own operation.&lt;/p&gt;

&lt;p&gt;The code to revert the whole pipeline was wrapped inside a transaction. It looked something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transactionScope&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_transactionManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsolationLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Serializable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RevertAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;transactionScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transactionScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Rollback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Something horribly horribly wrong happened&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Commit()&lt;/code&gt; method broke with the exception mentioned earlier. Arrrggg!&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1473158912295-779ef17fc94b?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjI0MTY1NTUw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Broken display glass&quot; /&gt;

&lt;figcaption&gt;No displays or electronic devices were damaged while debugging this issue. Photo by &lt;a href=&quot;https://unsplash.com/@shots_of_aspartame?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Julia Joppien&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/broken-computer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;always-await-async-methods&quot;&gt;Always await async methods&lt;/h2&gt;

&lt;p&gt;After Googling for a while, I found a couple of &lt;a href=&quot;https://stackoverflow.com/questions/36552285/the-transaction-operation-cannot-be-performed-because-there-are-pending-requests&quot;&gt;StackOverflow answers&lt;/a&gt; that mention to await all asynchronous methods. But, I thought it was a silly mistake and I started to look for something else more complicated.&lt;/p&gt;

&lt;p&gt;After checking for a while, trying to isolate the problem, following &lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;one of my debugging tips&lt;/a&gt;, something like this code got all my attention.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RevertAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReservationContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reservation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reservation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reservation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedByUserId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSystemUserAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_roomService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UnassignRooms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reservation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;updatedByUserId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSystemUserAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemUser&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_userRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetSystemUserAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Did you notice any asynchronous methods not being awaited? No? I didn’t for a while. Neither did my reviewers.&lt;/p&gt;

&lt;p&gt;But, there it was. Unnoticed for the code analyzer too. And, for all the passing tests.&lt;/p&gt;

&lt;p&gt;Oh, dear! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var updatedByUserId = GetSystemUserAsync(context).Id&lt;/code&gt;. This line was the root of the issue. It was meant to log the user Id who performed an operation, not the Id of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task&lt;/code&gt; not being awaited.&lt;/p&gt;

&lt;p&gt;Voilà! In case you have to face this exception, take a deep breath and carefully look for any async methods not being awaited inside your transactions.&lt;/p&gt;

&lt;p&gt;If you want to read more content, check &lt;a href=&quot;/2020/09/19/ThreeDebuggingTips/&quot;&gt;my debugging tips&lt;/a&gt;. To learn to write unit tests, start reading &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;. A better failing test would’ve caught this issue.&lt;/p&gt;

&lt;p&gt;To read about C# async/await keywords, check my &lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;C# Definitive Guide&lt;/a&gt;. It contains good resources to be a fluent developer in C#.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy bug fixing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Workplaces, studying and communication</title>
   <link href="https://canro91.github.io/2021/11/01/MondayLinks/"/>
   <updated>2021-11-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/11/01/MondayLinks</id>
   <content type="html">&lt;p&gt;Another Monday Links. Five articles I found interesting in last month.&lt;/p&gt;

&lt;h2 id=&quot;dont-waste-time-on-heroic-death-marches&quot;&gt;Don’t waste time on heroic death marches&lt;/h2&gt;

&lt;p&gt;“Successful companies, whether they’re programming houses, retailers, law firms, whatever, make their employees’ needs a priority.” Totally agree. No more comments! &lt;a href=&quot;https://www.computerworld.com/article/3630653/dont-waste-time-on-heroic-death-marches.html&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-study-effectively&quot;&gt;How to study effectively&lt;/h2&gt;

&lt;p&gt;One of my favorite subjects: how to study. Don’t cram. Don’t reread the material. Don’t highlight. Instead, study in short sessions and recall the material. Easy! There are even more strategies. &lt;a href=&quot;https://psyche.co/guides/how-research-from-psychology-can-help-you-study-effectively&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;undervalued-software-engineering-skills-writing-well&quot;&gt;Undervalued Software Engineering Skills: Writing Well&lt;/h2&gt;

&lt;p&gt;We, as developers, spend a lot of time writing prose, not only code. Commit messages, ticket and PR descriptions, README files. We should get better at it. To check my writings, I use the Hemingway app often. &lt;a href=&quot;https://blog.pragmaticengineer.com/on-writing-well/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1580196979617-7c9df939e6b6?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzMjc3ODE1MQ&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;women sitting on chairs&quot; /&gt;

&lt;figcaption&gt;Once upon a time, there were no Zoom calls. Photo by &lt;a href=&quot;https://unsplash.com/@bostonpubliclibrary?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Boston Public Library&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;15-signs-you-joined-the-wrong-company-as-a-developer&quot;&gt;15 signs you joined the wrong company as a developer&lt;/h2&gt;

&lt;p&gt;Number 12. and 13. are BIG red flags. Let’s pay attention to those. Recently, I read about “disagree with your feet.” It resonates with this article. When you don’t like something about your job and you can’t do anything about it, walk away. &lt;a href=&quot;https://livecodestream.dev/post/15-signs-you-joined-the-wrong-company-as-a-developer/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;no-we-wont-have-a-video-call-for-that&quot;&gt;No, we won’t have a video call for that&lt;/h2&gt;

&lt;p&gt;I don’t like those chat messages with only “Hi!” or “How are you?” when we both know that’s not the message. This article shows how to better communicate on remote teams. Embrace asynchronous communication. Prefer (in order) Issue tracker, Wiki, email, and chat. Stay away from video calls as much as possible. Don’t ping people on chat software. And other ideas. &lt;a href=&quot;https://xahteiwi.eu/resources/presentations/no-we-wont-have-a-video-call-for-that/&quot;&gt;Read full article&lt;/a&gt;, &lt;a href=&quot;https://media.ccc.de/v/froscon2020-2605-no_we_won_t_have_a_video_call_for_that&quot;&gt;Watch full presentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! This Monday Links ended up being about better workplaces. See you in a month or two in the next Monday Links! In the meantime, grab your own copy of my free eBook &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt;. Don’t miss the previous &lt;a href=&quot;/2021/09/06/MondayLinks/&quot;&gt;Monday Links on Farmers, Incidents and Holmes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy reading!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Dictionary keys are converted to lowercase too on serialization</title>
   <link href="https://canro91.github.io/2021/10/25/LowerCaseDictionaryKeysOnSerialization/"/>
   <updated>2021-10-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/10/25/LowerCaseDictionaryKeysOnSerialization</id>
   <content type="html">&lt;p&gt;Today, I needed to pass a dictionary between two ASP.NET Core 6.0 API sites. To my surprise, on the receiving side, I got the dictionary with all its keys converted to lowercase instead of PascalCase. I couldn’t find any element on the dictionary, even though the keys had the same names on each API site. This is what I learned about serializing dictionary keys.&lt;/p&gt;

&lt;h2 id=&quot;serialization-with-newtonsoftjson&quot;&gt;Serialization with Newtonsoft.Json&lt;/h2&gt;

&lt;p&gt;It turns out that the two API sites were using Newtonsoft.Json for serialization. Both of them used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CamelCasePropertyNamesContractResolver&lt;/code&gt; when adding Newtonsoft.Json.&lt;/p&gt;

&lt;p&gt;Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json.Serialization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddNewtonsoftJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SerializerSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullValueHandling&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NullValueHandling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ignore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SerializerSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContractResolver&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CamelCasePropertyNamesContractResolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//    ^^^^^&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// This is what I mean&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;With CamelCasePropertyNamesContractResolver, Newtonsoft.Json writes property names in camelCase. But, Newtonsoft.Json treats dictionary keys like properties too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That was the reason why I got my dictionary keys in lowercase. I used one-word names and Newtonsoft.Json made them camelCase.&lt;/p&gt;

&lt;p&gt;To prove this, let’s create a simple controller that read and writes a dictionary. Let’s do this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;LowerCaseDictionaryKeys.Controllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[controller]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DictionaryController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerBase&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpPost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyViewModel&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyViewModel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Just return the same input&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyViewModel&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, let’s notice in the output from Postman how the request and the response differ. The keys have a different case. Arggg!&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-10-25-LowerCaseDictionaryKeysOnSerialization/Before.png&quot; alt=&quot;Postman request and response bodies&quot; width=&quot;500px&quot; /&gt;
    &lt;figcaption&gt;Postman request and response bodies&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;1-configure-newtonsoftjson-naming-strategy&quot;&gt;1. Configure Newtonsoft.Json naming strategy&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;To preserve the case of dictionary keys with Newtonsoft.Json, configure the ContractResolver setting with CamelCaseNamingStrategy class and set its ProcessDictionaryKeys property to false.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When registering Newtonsoft.Json, in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SerializerSettings&lt;/code&gt; option, let’s do:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json.Serialization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Newtonsoft.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddNewtonsoftJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SerializerSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NullValueHandling&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NullValueHandling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ignore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SerializerSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContractResolver&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CamelCasePropertyNamesContractResolver&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;NamingStrategy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CamelCaseNamingStrategy&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;ProcessDictionaryKeys&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// Do not change dictionary keys casing&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For more details, see Newtonsoft.Json docs to &lt;a href=&quot;https://www.newtonsoft.com/json/help/html/NamingStrategySkipDictionaryKeys.htm&quot;&gt;Configure NamingStrategy dictionary serialization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After changing the naming strategy, let’s see the response of our sample controller. That’s what I wanted!&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-10-25-LowerCaseDictionaryKeysOnSerialization/After.png&quot; alt=&quot;Postman request and response after changing NamingStrategy&quot; width=&quot;500px&quot; /&gt;
    &lt;figcaption&gt;Postman request and response bodies&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;what-about-systemtextjson&quot;&gt;What about System.Text.Json?&lt;/h3&gt;

&lt;p&gt;To maintain case of dictionary keys with System.Text.Json, let’s set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DictionaryKeyPolicy&lt;/code&gt; property inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JsonSerializerOptions&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JsonNamingPolicy.CamelCase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program.cs&lt;/code&gt; class, let’s write,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddJsonOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JsonSerializerOptions&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DictionaryKeyPolicy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonNamingPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CamelCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For more naming policies, see Microsft docs to &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-customize-properties#camel-case-dictionary-keys&quot;&gt;customize property names and values with System.Text.Json&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-use-a-comparer-with-dictionaries&quot;&gt;2. Use a comparer with dictionaries&lt;/h2&gt;

&lt;p&gt;Another alternative is to use a dictionary with a comparer that ignores case of keys.&lt;/p&gt;

&lt;p&gt;On the receiving API site, let’s add an empty constructor on the request view model to initialize the dictionary with a comparer to ignore cases.&lt;/p&gt;

&lt;p&gt;In my case, I was passing a metadata dictionary between the two sites. I could use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringComparer.OrdinalIgnoreCase&lt;/code&gt; to create a dictionary ignoring the case of keys.&lt;/p&gt;

&lt;p&gt;This way, no matter the case of keys, I could find them when using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TryGetValue()&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyViewModel&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringComparer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                    ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dict&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how we can configure the case of dictionary keys when serializing requests and how to read dictionaries with keys no matter the case of its keys.&lt;/p&gt;

&lt;p&gt;If you want to read about ASP.NET Core, check &lt;a href=&quot;/2020/06/29/HowToAddACacheLayer/&quot;&gt;how to add a caching layer&lt;/a&gt; and &lt;a href=&quot;/2020/08/21/HowToConfigureValues/&quot;&gt;how to read your appsettings.json configuration file&lt;/a&gt;. To avoid KeyNotFoundException and other exceptions when working with dictionaries, check my &lt;a href=&quot;/2020/08/01/AnotherTwoCSharpIdiomsPart3/&quot;&gt;idioms on dictionaries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy C# time&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Don&apos;t duplicate logic in Asserts: The most common mistake on unit testing</title>
   <link href="https://canro91.github.io/2021/10/11/DontRepeatLogicInAssertions/"/>
   <updated>2021-10-11T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/10/11/DontRepeatLogicInAssertions</id>
   <content type="html">&lt;p&gt;We have covered some &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;common mistakes when writing unit tests&lt;/a&gt;. Some of them may seem obvious. But, we all have made this mistake when we started to write unit tests. This is the most common mistake when writing unit tests and how to fix it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t repeat the logic under test when verifying the expected result of tests. Instead, use known, hard-coded, pre-calculated values.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s write some tests for &lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;Stringie&lt;/a&gt;, a (fictional) library to manipulate strings with a fluent interface. Stringie has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method to remove substrings from the end of a string.&lt;/p&gt;

&lt;p&gt;We can use Stringie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hello&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;Hello,&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;dont-copy-and-paste-the-tested-logic&quot;&gt;Don’t Copy and Paste the tested logic&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When writing unit tests, don’t copy the tested logic and paste it into private methods to use them inside assertions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we bring the tested logic to private methods in our tests, we will have code and bugs in two places. Duplication is the root of all evil. Even, inside our tests.&lt;/p&gt;

&lt;p&gt;Please, don’t write assertions like the one in this test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveFromEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We duplicate the Remove logic in another method&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemoveFromEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IndexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1533046652171-aecb6943c03a?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYzMTcyMTIyMw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;building of apartments&quot; /&gt;

&lt;figcaption&gt;That&apos;s a lot of duplication. Photo by &lt;a href=&quot;https://unsplash.com/@joshchai?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Joshua  Chai&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;dont-make-internals-public&quot;&gt;Don’t make internals public&lt;/h2&gt;

&lt;p&gt;Also, by mistake, we expose the internals of the tested logic to use them in assertions. We make private methods public and static. Even to test those private methods directly.&lt;/p&gt;

&lt;p&gt;From our &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;, we learned to write unit tests through public methods. We should test the observable behavior of our tested code. A returned value, a thrown exception, or an external invocation.&lt;/p&gt;

&lt;p&gt;Again, don’t write assertions like the one in this test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stringie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PrivateMethodMadePublicAndStatic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// An &quot;internal&quot; method exposed to our tests &lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;use-known-values-to-assert&quot;&gt;Use known values to Assert&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Instead of duplicating the tested logic, by exposing internals or copy-pasting code into assertions, use a known expected value.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For our sample test, let’s simply use the expected substring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Hello,&quot;&lt;/code&gt;. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//              ^^^^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Let&apos;s use a known value in our assertions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we end up using the same expected values, we can create constants for them. Like,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hello&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HelloAndComma&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s the most common mistake when writing unit tests. It seems silly! But often, we duplicate Math operations and string concatenations and it passes unnoticed. Remember, don’t put too much logic in your tests. Tests should be only assignments and method calls.&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read my post on &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;how to write your first unit tests in C# with MSTest&lt;/a&gt; and check the &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing your first tests&lt;/a&gt;. Also, don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like the ones from this post.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two C# idioms: On defaults and switch</title>
   <link href="https://canro91.github.io/2021/09/27/TwoCSharpIdiomsPart4/"/>
   <updated>2021-09-27T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/09/27/TwoCSharpIdiomsPart4</id>
   <content type="html">&lt;p&gt;In this part of the C# idioms series, we have one idiom to write more intention-revealing defaults and another idiom to convert mapping code using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; to a more compact alternative using a dictionary.&lt;/p&gt;

&lt;h2 id=&quot;use-intention-revealing-defaults&quot;&gt;Use intention-revealing defaults&lt;/h2&gt;

&lt;p&gt;When initializing variables to default values, use intention-revealing alternatives.&lt;/p&gt;

&lt;p&gt;Are you initializing a string variable to later assign it? Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&quot;&lt;/code&gt;. Do you want to return an empty string from a method? Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string.Empty&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same is true for collections. If you’re initializing a collection to later add some elements, use the normal constructors like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new string[length];&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new List&amp;lt;string&amp;gt;();&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But, if you want to return an empty collection. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array.Empty&amp;lt;string&amp;gt;()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable.Empty&amp;lt;string&amp;gt;()&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;replace-switch-with-a-dictionary&quot;&gt;Replace switch with a dictionary&lt;/h2&gt;

&lt;p&gt;Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; mapping two types with a dictionary.&lt;/p&gt;

&lt;p&gt;Turn every value in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;case&lt;/code&gt; statements into a key in the dictionary. And, turn the returned value in every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;case&lt;/code&gt; into the value of the matching key in the dictionary.&lt;/p&gt;

&lt;p&gt;To replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; case, take advantage of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TryGetValue()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetValueOrDefault()&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;Before, to map from a credit card brand name in strings to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CardType&lt;/code&gt; enum, we did this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapToCardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Visa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Mastercard&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MasterCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;American Express&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AmericanExpress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unknown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After, replacing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapToCardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardTypeMappings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Visa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visa&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Mastercard&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mastercard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;American Express&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AmericanExpress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardTypeMappings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unknown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;c-80-and-dictionaries&quot;&gt;C# 8.0 and Dictionaries&lt;/h3&gt;

&lt;p&gt;Also, we can use the newer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; syntaxt from &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;C# version 8.0&lt;/a&gt; to write more compact &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Starting from C# version 8.0, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; are expressions. It means we can assign &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; to variables and use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; as returned values.&lt;/p&gt;

&lt;p&gt;Notice how we use a discard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; case to throw an exception.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapToCardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;Visa&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;MasterCard&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MasterCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;American Express&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AmericanExpress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unknown&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! These are the C# idioms for today. Remember to use intention-revealing defaults and take advantage of the new C# features for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to check more C# recent features, check &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;my Top 16 newest C# features&lt;/a&gt;. To get rid of exceptions when working with dictionaries, check &lt;a href=&quot;/2020/08/01/AnotherTwoCSharpIdiomsPart3/&quot;&gt;Idioms on Dictionaries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And, don’t miss the other C# Idioms.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My Top 16 newest C# features by version</title>
   <link href="https://canro91.github.io/2021/09/13/TopNewCSharpFeatures/"/>
   <updated>2021-09-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/09/13/TopNewCSharpFeatures</id>
   <content type="html">&lt;p&gt;C# is a language in constant evolution. It has changed a lot since its initial versions in the early 2000’s. Every version brings new features to write more concise and readable code. These are some C# features I like the most and use often. Hope you find them useful too.&lt;/p&gt;

&lt;p&gt;Let’s start with the best C# features by version, starting from version 6.&lt;/p&gt;

&lt;h2 id=&quot;c-60&quot;&gt;C# 6.0&lt;/h2&gt;

&lt;h3 id=&quot;string-interpolation-hello-name&quot;&gt;String interpolation: $”Hello, {name}”&lt;/h3&gt;

&lt;p&gt;Before with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string.Format()&lt;/code&gt;, we could miss a parameter or add them in the wrong order. If we forgot a parameter, we will get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FormatException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With string interpolation, we can inline variables directly in the string we want to build. To use string interpolation, before the opening quote of your string, let’s add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt; and wrap our variables around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string.Format()&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, {0} {1}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, if we forgot to add one parameter,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, {0} {1}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*, I forgot to add the name parameter*/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// System.FormatException:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//    Index (zero based) must be greater than or equal to zero and less than the size of the argument list.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After with string interpolation,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Hello, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, it’s clearer if we’re missing a parameter or if we have them in the wrong order.&lt;/p&gt;

&lt;h3 id=&quot;null-conditional--and-null-coalescing-operators-&quot;&gt;Null-conditional (?.) and null-coalescing operators (??)&lt;/h3&gt;

&lt;p&gt;Starting from C# 6.0, we have two new operators: null-conditional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?.&lt;/code&gt; and null-coalescing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt; operators. These two new operators helps us to get rid of null values and &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;NullReferenceException&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the null-conditional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?.&lt;/code&gt; operator, we access a member’s object if the object isn’t null. Otherwise, it returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The null-coalescing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt; operator evaluates an alternative expression if the first one is null.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadNameFromSomewhere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadNameFromSomewhere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It executes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Trim()&lt;/code&gt; only if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. Otherwise, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name?.Trim()&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. But, with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt; operator, the whole expression returns “none”.&lt;/p&gt;

&lt;h3 id=&quot;expression-body-definition-&quot;&gt;Expression body definition (=&amp;gt;)&lt;/h3&gt;

&lt;p&gt;Now, one-line functions are truly one liners. We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&amp;gt;&lt;/code&gt; to declare the body of methods and properties in a single line of code.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MeaningOfLife&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MeaningOfLife&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;nameof-expression&quot;&gt;nameof expression&lt;/h3&gt;

&lt;p&gt;As its name implies, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nameof&lt;/code&gt; operator returns the name of a variable, type or member as a string. It makes renaming things easier.&lt;/p&gt;

&lt;p&gt;Before without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nameof&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;param1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nameof&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                              ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;c-7x&quot;&gt;C# 7.X&lt;/h2&gt;

&lt;h3 id=&quot;throw-expressions&quot;&gt;Throw expressions&lt;/h3&gt;

&lt;p&gt;Now, throws are expressions. It means we can use them inside conditionals and null coalescing expressions.&lt;/p&gt;

&lt;p&gt;We can combine the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;throw&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nameof&lt;/code&gt; operators to check required parameters inside constructors. For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Director&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;director&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;director&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                      ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, how the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt; operator evaluates the expression on the right, which is a throw.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// System.ArgumentNullException: Value cannot be null.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Parameter name: director&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;out-variables&quot;&gt;out variables&lt;/h3&gt;

&lt;p&gt;We can inline the variable declaration next to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;out&lt;/code&gt; keyword using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;Before, we had to declare a variable in a separate statement,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readFromKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After, inlining the variable declaration,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readFromKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                            ^^^ &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of declaring a variable, we can use discards &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; to ignore the output value. For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readFromKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m not a big fan of methods with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;out&lt;/code&gt; references. But, with this feature I like them a bit more. I prefer tuples.&lt;/p&gt;

&lt;h3 id=&quot;tuples&quot;&gt;Tuples&lt;/h3&gt;

&lt;p&gt;Speaking of tuples…Now we can access tuple members by name. We don’t need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Item1&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Item2&lt;/code&gt; anymore.&lt;/p&gt;

&lt;p&gt;We can declare tuples wrapping its members inside parenthesis. For example, to declare a pair of coordinates, it would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(int X, int Y) origin = (0, 0)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can use named members when declaring methods and deconstructing returned values.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Item1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Salutation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even better,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Salutation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Salutation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Do you remember discards? We can use them with tuples too.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Greet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;asynchronous-main-methods&quot;&gt;Asynchronous Main methods&lt;/h3&gt;

&lt;p&gt;Now, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async&lt;/code&gt; Main methods are available in Console applications.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAwaiter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;pattern-matching&quot;&gt;Pattern matching&lt;/h3&gt;

&lt;p&gt;With pattern matching, we have more flexibility in control flow structures like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt;. Let’s see a couple of examples.&lt;/p&gt;

&lt;p&gt;On one hand, we can avoid casting types inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;Before without pattern matching, we needed to cast types,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SalaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SalaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After, with pattern matching, we can declare a variable in the condition,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SalaryEmployee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                             ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On another hand, we can use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;when&lt;/code&gt; clause inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before, we had to rely on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statements inside the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;case&lt;/code&gt;, like this&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SalaryEmployee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Salary&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;DoSomethingElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// other cases...        &lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, with pattern matching, we can have separate cases,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;employee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SalaryEmployee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Salary&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//      ^^^^&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SalaryEmployee&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoSomethingElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salaryEmployee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// other cases...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I found it more readable this way. Let’s keep the conditional case before the one without conditions.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1569783899817-a49d2d25287c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjIwNjc4MTQx&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Tools on a workbench&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@oxaroxa?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Oxa Roxa&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/tools?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;c-80&quot;&gt;C# 8.0&lt;/h2&gt;

&lt;h3 id=&quot;switch-expressions&quot;&gt;switch expressions&lt;/h3&gt;

&lt;p&gt;Speaking of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; statements, starting from C# 8.0 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; are expressions. It means we can assign a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; to a variable or return a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; from a method.&lt;/p&gt;

&lt;p&gt;Before a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; looked like this one,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Visa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MasterCard&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MasterCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;American Express&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AmericanExpress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; as expressions,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;Visa&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Visa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;MasterCard&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MasterCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;American Express&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CardType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AmericanExpress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cardBrand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Switch expressions are more compact, right? Did you notice we assigned the result of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cardType&lt;/code&gt; variable? Cool!&lt;/p&gt;

&lt;h3 id=&quot;indices-and-ranges&quot;&gt;Indices and ranges&lt;/h3&gt;

&lt;p&gt;If you have used negative indices in Python, you would find this feature familiar. In Python, we use  negative indices to reference elements from the end of lists.&lt;/p&gt;

&lt;p&gt;We have a similar feature in C#, not with negative indices, but with the &lt;strong&gt;index from end&lt;/strong&gt; operator, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the index from end operator, the last element of an array would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array[^1]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before, we had to substract from the length of the array to access an element from the end. The last element of an array was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array[array.Length - 1]&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helloWorld&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;helloWorld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;helloWorld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &quot;world!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After, with the index from end operator,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helloWorld&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;helloWorld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[^&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &quot;world!&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the same spirit, we have ranges. An array without its last element would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array[0..^1]&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helloWorld&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helloWorld&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.^&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Hello,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;null-coalescing-assignment-&quot;&gt;Null-coalescing assignment (??=)&lt;/h3&gt;

&lt;p&gt;Do you remember the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?.&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??&lt;/code&gt; operators? Now, there is another operator to work with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. The &lt;strong&gt;null-coalescing assignment&lt;/strong&gt; operator, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;??=&lt;/code&gt;. It only assigns a variable if its value isn’t null.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magicNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadMagicNumberFromSomewhere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;magicNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;magicNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magicNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadMagicNumberFromSomewhere&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;magicNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;using-declarations&quot;&gt;Using declarations&lt;/h3&gt;

&lt;p&gt;A variable preceded by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt; is disposed at the end of the scope. We can get rid of the parethensis  around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt; statements and the brackets wrapping its body.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  
        &lt;span class=&quot;c1&quot;&gt;// Do something  &lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;  
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  
    &lt;span class=&quot;c1&quot;&gt;// Do something  &lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;nullable-reference-types&quot;&gt;Nullable reference types&lt;/h3&gt;

&lt;p&gt;With C# 8.0, all reference variables are non-nullable by default. Any attempt to dereference a nullable reference gets a warning from the compiler. Goodbye, NullReferenceException!&lt;/p&gt;

&lt;p&gt;To declare a variable that can be null, we need to add to its type declaration an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt;. The same way we have always declared nullable value types like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int?&lt;/code&gt;. For example, a nullable string would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string? canBeNull;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is a breaking change. We need to turn on this feature at the project level. To do so, let’s add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;&lt;/code&gt; inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PropertyGroup&lt;/code&gt; in our csproj files.&lt;/p&gt;

&lt;p&gt;For a console application, the csproj file with this feature turned on look like this:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--      ^^^^^^    --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before, if we access a member of a null reference, we get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NullReferenceException&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;SayHi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;lt;- System.NullReferenceException&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// System.NullReferenceException: &apos;Object reference not set to an instance of an object.&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// name was null.&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SayHi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But now, we get a compiler warning,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// warning CS8600: Converting null literal or possible null value to non-nullable type.&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;canBeNullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;SayHi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// warning CS8604: Possible null reference argument for parameter &apos;name&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To get rid of the compiler warning, we have to check for null values first.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;canBeNullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;canBeNullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;SayHi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;c-90&quot;&gt;C# 9.0&lt;/h2&gt;

&lt;h3 id=&quot;records&quot;&gt;Records&lt;/h3&gt;

&lt;p&gt;A record is an immutable reference type with built-in equality methods. When we create a record, the compiler creates &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToString()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode()&lt;/code&gt;, value-based equality methods, a copy constructor and a deconstructor.&lt;/p&gt;

&lt;p&gt;Records are helpful to replace value-objects in our code.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;top-level-statements&quot;&gt;Top-level statements&lt;/h3&gt;

&lt;p&gt;All the boilerplate code is now gone from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Main&lt;/code&gt; methods. It gets closer to scripting languages like Python and Ruby.&lt;/p&gt;

&lt;p&gt;Before, to write the “Hello, world!” program in C#, we needed to bring namespaces, classes, methods and arrays only to print a message out to the console.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HelloWorld&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After, it boils down to only two lines.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;c-100&quot;&gt;C# 10.0&lt;/h2&gt;

&lt;h3 id=&quot;file-scoped-namespace-declaration&quot;&gt;File-scoped namespace declaration&lt;/h3&gt;

&lt;p&gt;With C# 10.0, we can simplify namespace declaration inside our classes.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After, we can reduce the level of indentations by using a semicolon on the namespace declaration,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Movie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;throwifnull&quot;&gt;ThrowIfNull&lt;/h3&gt;

&lt;p&gt;ArgumentNullException has a helper &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThrowIfNull()&lt;/code&gt; to help us check for required parameters in our methods.&lt;/p&gt;

&lt;p&gt;Before,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                              ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowIfNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                    ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;global-using-statements&quot;&gt;Global using statements&lt;/h3&gt;

&lt;p&gt;C# 10.0 reduces the boilerplate from our classes even further by hiding common using declarations.&lt;/p&gt;

&lt;p&gt;Before, a “Hello, world” program looked like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HelloWorld&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, with Top-level statements and global using statements, it’s a single line of code,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a feature is enabled by default, but we can turn it off in our csproj files. For example, this is the csproj file of a Console app with global using statements and nullable references.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net6.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--            ^^^^^^    --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! These are the C# features I like the most. Which ones didn’t you know about? Which ones you use most often? What features would you like to see in future versions?&lt;/p&gt;

&lt;p&gt;Do you want to learn more about the C# language? Check my &lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;C# Definitive Guide&lt;/a&gt;. It contains the subjects I believe all intermediate C# developers should know. Are you new to LINQ? Check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;quick guide to LINQ with examples&lt;/a&gt;. And, don’t miss my &lt;a href=&quot;/2019/11/19/TwoCSharpIdioms/&quot;&gt;C# idioms series&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Monday Links: Farmers, Incidents &amp; Holmes</title>
   <link href="https://canro91.github.io/2021/09/06/MondayLinks/"/>
   <updated>2021-09-06T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/09/06/MondayLinks</id>
   <content type="html">&lt;p&gt;Today I want to start a new series: Monday Links. I want to share interesting and worth-sharing blog posts, articles or anything I read online in the last month or two. You may find them interesting and instructive too.&lt;/p&gt;

&lt;h2 id=&quot;farmers-always-worked-from-home&quot;&gt;Farmers always Worked From Home&lt;/h2&gt;

&lt;p&gt;We’re in the work-from-home era. But, farmers have always worked from home. This article states we all got the work-life balance wrong. Speaking about a farmer neighbor…“He does what he needs to, and rests in between”. &lt;a href=&quot;https://piszek.com/2021/07/26/farmers-work-from-home/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;an-old-hackers-tips-on-staying-employed&quot;&gt;An Old Hacker’s Tips On Staying Employed&lt;/h2&gt;

&lt;p&gt;An ode to personal brand. Build a reputation that will take of you on rough times. The “Two and Done” principle was my favorite. When making a decision, present your case only twice. After that, say that you have outlined your options and you want to move on taking somebody else’s idea. &lt;a href=&quot;https://madned.substack.com/p/an-old-hackers-tips-on-staying-employed&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Apollo_13_Mailbox_at_Mission_Control.jpg/640px-Apollo_13_Mailbox_at_Mission_Control.jpg&quot; alt=&quot;MOCR 2 during the Apollo 13 crisis&quot; width=&quot;600&quot; /&gt;

&lt;figcaption&gt;Houston, we have a problem! Photo by &lt;a rel=&quot;nofollow&quot; href=&quot;http://spaceflight.nasa.gov/gallery/images/apollo/apollo13/html/s70-35013.html&quot;&gt;NASA&lt;/a&gt;, &lt;a href=&quot;https://commons.wikimedia.org/w/index.php?curid=6641252&quot;&gt;Public Domain&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;secrets-of-great-incident-management&quot;&gt;Secrets of great incident management&lt;/h2&gt;

&lt;p&gt;Incident commander, driver, communicator, recorder, researcher…It reminds me one of those movies about space missions. “Houston, we have a problem!” Outages are stressful situations for sure. People running, pointing fingers, making phone calls…the worst thing is when only you figure out you have an outage when you get a phone call from one of your clients. Arrggg! &lt;a href=&quot;https://bitfieldconsulting.com/blog/got-game-secrets-of-great-incident-management&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;patterns-in-confusing-explanations&quot;&gt;Patterns in confusing explanations&lt;/h2&gt;

&lt;p&gt;It explains how not to explain things. I felt guilty of doing some of these anti-patterns. The ones about analogies, ignoring the whys…One piece of advise I liked was to write for one person in mind. Imagine a friend or coworker and explain the subject to her. &lt;a href=&quot;https://jvns.ca/blog/confusing-explanations/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-programmers-can-learn-from-sherlock-holmes&quot;&gt;What Programmers Can Learn From Sherlock Holmes&lt;/h2&gt;

&lt;p&gt;What Sherlock Home stories and programming have in common. Not all facts are obvious to everyone. We all have one Lestrade in our programmer’s life. I liked those two things. &lt;a href=&quot;https://web.archive.org/web/20210925125821/https://codingtofreedom.com/what-programmers-can-learn-from-sherlock-holmes/&quot;&gt;Read full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! These were my most favorites reads from the last couple of months. See you in a month or two in the next Monday Links! In the mean time, stay tune to my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series to learn from &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;how to write your first unit tests with MSTest&lt;/a&gt; to &lt;a href=&quot;/2021/08/16/WriteCustomAssertions/&quot;&gt;how to write custom assertions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy reading!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unit Testing 101: From Zero to Hero</title>
   <link href="https://canro91.github.io/UnitTesting"/>
   <updated>2021-08-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/UnitTesting</id>
   <content type="html">&lt;p&gt;Do you want to start writing unit tests, but don’t know where to start? Do you want to adopt unit testing in your team? Keep reading!&lt;/p&gt;

&lt;p&gt;If you’re a beginner or a seasoned developer new to unit testing, this is the place for you.&lt;/p&gt;

&lt;h2 id=&quot;write-it&quot;&gt;Write it&lt;/h2&gt;

&lt;p&gt;Learn to &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;write your first unit tests with MSTest&lt;/a&gt;. Read what a unit test is, why we need unit tests, and what makes a good unit test. And understand &lt;a href=&quot;/2024/11/30/TestingPrivateMethods/&quot;&gt;why we shouldn’t try to test private methods&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Identify and fix these &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;four common mistakes&lt;/a&gt; when writing your first unit tests. Learn one of these &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;four naming conventions&lt;/a&gt; and stick to it. Don’t worry about long test names.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;I&apos;ve expanded the first posts in this series (and added more lessons) in &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UnitTesting101eBook-Link&quot;&gt;&lt;em&gt;Start Testing&lt;/em&gt;&lt;/a&gt;, your step-by-step guide to writing C# unit tests with confidence.&lt;/div&gt;

&lt;figure&gt;
&lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; data-goatcounter-click=&quot;UnitTesting101eBook-Image&quot;&gt;&lt;img src=&quot;/assets/posts/2021-08-30-UnitTesting/GrabYourOwnCopy.png&quot; alt=&quot;Grab your own copy of Unit Testing 101&quot; /&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;When writing your unit tests, make sure you &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;don’t duplicate logic in Asserts&lt;/a&gt;. That’s THE most common mistake in unit testing. Tests should only contain assignments and method calls.&lt;/p&gt;

&lt;h2 id=&quot;improve-it&quot;&gt;Improve it&lt;/h2&gt;

&lt;p&gt;Learn how to write good unit tests, avoiding &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;complex setup scenarios and hidden test values&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure to always &lt;a href=&quot;/2021/02/05/FailingTest/&quot;&gt;write a failing test first&lt;/a&gt;. And make it fail for the right reasons.&lt;/p&gt;

&lt;p&gt;Write tests easy to follow &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;using simple test values&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Use &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;Builders to create test data&lt;/a&gt;. And, learn &lt;a href=&quot;/2021/05/10/WriteTestsThatUseDateTimeNow/&quot;&gt;how to write tests that use DateTime.Now&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Strive for a set of always-passing tests, a “Safe Green Zone.” For example, &lt;a href=&quot;/2020/12/04/UseCultureWhenParsing/&quot;&gt;use a culture when parsing numeric strings&lt;/a&gt;, instead of relying on a default culture on developers’ machines.&lt;/p&gt;

&lt;h2 id=&quot;fake-it&quot;&gt;Fake it&lt;/h2&gt;

&lt;p&gt;Learn &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;what fakes are in unit testing&lt;/a&gt; and the difference between stubs and mocks. Follow these &lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;tips for better stubs and mocks in C#&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Read &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;how to create fakes with Moq&lt;/a&gt;, an easy to use mocking library.&lt;/p&gt;

&lt;p&gt;If you find yourself using lots of fakes, take advantage of &lt;a href=&quot;/2021/06/21/WriteSimplerTestsTypeBuilderAndAutoFixture/&quot;&gt;automocking with TypeBuilder and AutoFixture&lt;/a&gt; to write simpler tests.&lt;/p&gt;

&lt;h2 id=&quot;master-it&quot;&gt;Master it&lt;/h2&gt;

&lt;p&gt;Read all the tips from this series on &lt;a href=&quot;/2021/07/05/UnitTestingBestPractices/&quot;&gt;Unit Testing Best Practices&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Deep into assertions, check &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;how to write better assertions&lt;/a&gt; and &lt;a href=&quot;/2021/08/16/WriteCustomAssertions/&quot;&gt;how to write custom assertions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To see how to put these best practices in place, see how I refactored these real-world tests:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/08/02/LetsRefactorATest/&quot;&gt;generating report in a payment system&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/12/08/TestingOAuthConnections/&quot;&gt;storing and updating OAuth connections&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/12/22/TestingDuplicatedEmails/&quot;&gt;removing duplicated email addresses&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/05/15/TestingEmailStatusUpdates/&quot;&gt;updating email statuses&lt;/a&gt;, and,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/05/29/SpeedingUpSomeTests/&quot;&gt;speeding up a slow test suite&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work with ASP.NET Core, learn how to write tests for &lt;a href=&quot;/2022/12/01/TestingHttpClient/&quot;&gt;HttpClient&lt;/a&gt;, &lt;a href=&quot;/2022/12/03/TestingAspNetAuthorizationFilters/&quot;&gt;Authorization filters&lt;/a&gt;, and &lt;a href=&quot;/2022/12/04/TestingLoggingAndLogMessages/&quot;&gt;logging and logging messages&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Write custom Assertions to improve your tests</title>
   <link href="https://canro91.github.io/2021/08/16/WriteCustomAssertions/"/>
   <updated>2021-08-16T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/08/16/WriteCustomAssertions</id>
   <content type="html">&lt;p&gt;Last time, we went through some best practices to &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;write better assertions&lt;/a&gt; on our tests. This time, let’s focus on how to use custom assertions to improve the readability of our tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use custom assertions to encapsulate multiple assertions on a single method and express them in the same language as the domain model. Write custom assertions with local methods or extension methods on the result object of the tested method or on the fake objects.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can either create custom assertions on top of the MSTest &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; class. And, our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify&lt;/code&gt; methods on Moq mocks.&lt;/p&gt;

&lt;h2 id=&quot;1-how-to-write-custom-mstest-assert-methods&quot;&gt;1. How to write custom MSTest Assert methods&lt;/h2&gt;

&lt;p&gt;Let’s refactor one of our tests for &lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;Stringie&lt;/a&gt;, a (fictional) library to manipulate strings. We used Stringie to learn &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing tests&lt;/a&gt; and &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;4 test naming conventions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write custom assertions with MSTest, write an extension method on top of the Assert class. Then, compare the expected and actual parameters and throw an AssertFailedException if the comparison fails.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringIsEmpty()&lt;/code&gt; method,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomAssert&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringIsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AssertFailedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Expect empty string but was &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringIsEmpty()&lt;/code&gt; with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;That&lt;/code&gt; property. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;That&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringIsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this custom assertion in place, we can rewrite the Assert part of our tests for the Stringie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove&lt;/code&gt; method. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie.UnitTests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_NoParameters_ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;That&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;StringIsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//          ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With custom assertions, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringIsEmpty()&lt;/code&gt;, we can write our assertions using the same vocabulary from our business domain.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1414497729697-b8555ba6c1cc?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYyNzA1MDM4Mw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Shaving wood&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@asthetik?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Mike Kenneally&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/wood-workshop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-how-to-write-custom-moq-verify-method&quot;&gt;2. How to write custom Moq Verify method&lt;/h2&gt;

&lt;p&gt;If we’re using Moq, we can create our custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; methods too.&lt;/p&gt;

&lt;p&gt;Let’s write some tests for an API client. This time, we have a payment processing system and we want to provide our users a user-friendly client to call our endpoints.&lt;/p&gt;

&lt;p&gt;We want to test that our methods call the right API endpoints. If we change the client version number, we should include the version number in the endpoint URL.&lt;/p&gt;

&lt;p&gt;We could write some unit tests like these ones,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CustomAssertions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentProxyTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PayAsync_ByDefault_CallsLatestVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PayAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Here we verify we called the right url&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApiResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     ^^^^^&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbsoluteUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/v2/pay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PayAsync_VersionNumber_CallsEndpointWithVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PayAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Here we verify we called the right url again&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApiResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//     ^^^^^            &lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbsoluteUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/v1/pay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnyPaymentRequest&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// All initializations here...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These tests rely on &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;Moq to write fakes&lt;/a&gt; and &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;object mothers to create test data&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Notice, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; methods in the two tests. Did you notice how buried inside all that boilerplate is the URL we want to check? That’s what we’re interested in. It would be nice if we had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyItCalled()&lt;/code&gt; method and we just passed a string or URI with the URL we want.&lt;/p&gt;

&lt;p&gt;Let’s create an extension method on top of our fake. Let’s write the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyItCalled()&lt;/code&gt; method we want. It will receive a relative URL and call Moq &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; method. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CustomAssertions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MockApiClientExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;VerifyItCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relativeUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApiResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbsoluteUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;relativeUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VerifyItCalled()&lt;/code&gt; in place, let’s refactor our tests to use it,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CustomAssertions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentProxyTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PayAsync_ByDefault_CallsLatestVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PayAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Now, it&apos;s way more readable&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyItCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/v2/pay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PayAsync_VersionNumber_CallsEndpointWithVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PayAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyPaymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// No more boilerplate code to check things&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fakeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VerifyItCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/v1/pay&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AnyPaymentRequest&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentRequest&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// All initializations here...&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With our custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; method, our tests are more readable. And, we wrote the tests in the same terms as our domain language. No more ceremony to check we called the right url.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to use custom assertions to write tests in the same terms as your domain model.&lt;/p&gt;

&lt;p&gt;In case you have plain assertions, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; methods with Moq, simply write private methods to group your assertions and share them in a base test class. Or write extension methods on the output of the method being tested. For more details on this technique, check xUnitPatterns on &lt;a href=&quot;http://xunitpatterns.com/Custom%20Assertion.html&quot;&gt;Custom Assertions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re new to fakes, mocks, and stubs, read &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;what are fakes in unit testing&lt;/a&gt; and &lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;how to write better stubs and mocks&lt;/a&gt;. Also, don’t miss my &lt;a href=&quot;/2021/07/05/UnitTestingBestPractices/&quot;&gt;unit testing best practices&lt;/a&gt; and the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Let&apos;s refactor a test: Payment report</title>
   <link href="https://canro91.github.io/2021/08/02/LetsRefactorATest/"/>
   <updated>2021-08-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/08/02/LetsRefactorATest</id>
   <content type="html">&lt;p&gt;Let’s refactor a test to follow our &lt;a href=&quot;/2021/07/05/UnitTestingBestPractices/&quot;&gt;unit testing best practices&lt;/a&gt;. This test is based on a real test I had to modify in one of my client’s projects.&lt;/p&gt;

&lt;p&gt;Imagine this test belongs to a payment gateway. The system takes payments on behalf of partners. At the end of every month, the partners get the collected payments discounting any fees. This process is called a payout.&lt;/p&gt;

&lt;p&gt;Partners can generate a report with all the transactions associated with a payout. This report can show dates at a different timezone if the user wants it.&lt;/p&gt;

&lt;p&gt;This is the test we’re going to refactor. This test is for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetPayoutDetailsAsync()&lt;/code&gt; method. This method finds the payouts in a date range. Then, it shows all transactions related to those payouts. This method feeds a report in Microsoft Excel or any other spreadsheet software.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync_HappyPath_SuccessWithoutTimezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payouts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;balanceTransactions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payments&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PayoutRequest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PageSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AccountId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AnyAccountId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;StartDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EndDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PayoutDetailsService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;balanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPayoutWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPayoutsByDateRangeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBalanceTransactionWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetBalanceTransactionsByPayoutsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This test uses &lt;a href=&quot;/2021/06/21/WriteSimplerTestsTypeBuilderAndAutoFixture/&quot;&gt;automocking with TypeBuilder&lt;/a&gt;. This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&amp;lt;T&amp;gt;&lt;/code&gt; creates an instance of a class with its dependencies replaced by &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;fakes using Moq&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, this test uses the &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;Builder pattern&lt;/a&gt; to create fakes with some test values before building a new instance. This test relies on object mothers to create input values for the stubs.&lt;/p&gt;

&lt;h2 id=&quot;1-separate-the-arrangeactassert-parts&quot;&gt;1. Separate the Arrange/Act/Assert parts&lt;/h2&gt;

&lt;p&gt;Let’s start by grouping related code to follow &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;the Arrange/Act/Assert (AAA) principle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To achieve this, let’s declare variables near their first use and inline the ones used in a single place.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync_HappyPath_SuccessWithoutTimezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Notice we inlined all input variables&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PayoutDetailsService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Notice we moved the request variable near its first use&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PayoutRequest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PageSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AccountId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AnyAccountId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;StartDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EndDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPayoutWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPayoutsByDateRangeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBalanceTransactionWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetBalanceTransactionsByPayoutsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we inlined all input variables and move the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;request&lt;/code&gt; variable closer to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetPayoutDetailsAsync()&lt;/code&gt; method where it’s used.&lt;/p&gt;

&lt;p&gt;Remember, &lt;strong&gt;declare variables near their first use&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-show-the-scenario-under-test-and-the-expected-result&quot;&gt;2. Show the scenario under test and the expected result&lt;/h2&gt;

&lt;p&gt;Now, let’s look at the test name.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync_HappyPath_SuccessWithoutTimezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Notice the test name...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// The rest of the test remains the same,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// but not for too long&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It states the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetPayoutDetailsAsync()&lt;/code&gt; method should work without a timezone. That’s the scenario of our test.&lt;/p&gt;

&lt;p&gt;Let’s follow the &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;“UnitOfWork_Scenario_ExpectedResult” naming convention&lt;/a&gt; to show the scenario under test in the middle part of the test name.&lt;/p&gt;

&lt;p&gt;Also, let’s avoid the filler word “Success”. In this test, success means the method returns the details without showing the transactions in another timezone. We learned to avoid filler words in our test names when we learned the &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s rename our test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync_NoTimeZone_ReturnsDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Test body remains the same...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this refactoring, it’s a good idea to add another test passing a timezone and checking that the found transactions are in the same timezone.&lt;/p&gt;

&lt;h2 id=&quot;3-make-test-value-obvious&quot;&gt;3. Make test value obvious&lt;/h2&gt;

&lt;p&gt;In the previous refactor, we renamed our test to show it works without a timezone.&lt;/p&gt;

&lt;p&gt;Anyone reading this test should expect a variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timezone&lt;/code&gt; assigned to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; or a method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithoutTimeZone()&lt;/code&gt; in a builder. Let’s &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;make the test value explicit&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync_NoTimeZone_ReturnsDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PayoutDetailsService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PayoutRequest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PageSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AccountId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AnyAccountId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;StartDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EndDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Notice we explicitly set no timezone&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TimeZone&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//        ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPayoutWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPayoutsByDateRangeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IBalanceTransactionWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetBalanceTransactionsByPayoutsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we have more than one test without a timezone, we can use a constant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NoTimeZome&lt;/code&gt; or an object mother for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PayoutRequest&lt;/code&gt;, something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NoTimeZonePayoutRequest&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;4-remove-over-specification&quot;&gt;4. Remove over-specification&lt;/h2&gt;

&lt;p&gt;For our last refactor, let’s remove those &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verify()&lt;/code&gt; calls. We don’t need them. &lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;We don’t need to assert on stubs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If any of the stubs weren’t in place, probably we will get a &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;NullReferenceException&lt;/a&gt; somewhere in our code. Those extra verifications make our test harder to maintain.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync_NoTimeZone_ReturnsDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PayoutDetailsService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestPayouts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestBalanceTransactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestPayments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PayoutRequest&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PageSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AccountId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AnyAccountId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateRange&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;StartDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EndDate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TimeZone&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPayoutDetailsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We stopped verifying on stubs&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That looks better! Unit tests got our back when changing our code. It’s better to keep them clean too. They are our safety net.&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;, &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing your first tests&lt;/a&gt; and &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;4 test naming conventions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more advanced content on unit testing, check &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;what are fakes in unit testing&lt;/a&gt; and don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to write better assertions in your tests</title>
   <link href="https://canro91.github.io/2021/07/19/WriteBetterAssertions/"/>
   <updated>2021-07-19T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/07/19/WriteBetterAssertions</id>
   <content type="html">&lt;p&gt;There’s a lot to say about how to &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;write good unit tests&lt;/a&gt;. This time, let’s focus on best practices to write better assertions on our tests.&lt;/p&gt;

&lt;p&gt;Here you have 5 tips to write better assertions on your unit tests.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;TL;DR&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;Follow the Arrange/Act/Assert (AAA) pattern&lt;/li&gt;
    &lt;li&gt;Separate each A of the AAA pattern with line breaks&lt;/li&gt;
    &lt;li&gt;Don’t put logic in your assertions&lt;/li&gt;
    &lt;li&gt;Have a single Act and Assert parts in each test&lt;/li&gt;
    &lt;li&gt;Use the right Assertion methods&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;1-follow-the-arrangeactassert-aaa-pattern&quot;&gt;1. Follow the Arrange/Act/Assert (AAA) pattern&lt;/h2&gt;

&lt;p&gt;If you could take home only one thing: &lt;strong&gt;follow the Arrange/Act/Assert (AAA) pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Arrange/Act/Assert (AAA) pattern states that each test should contain three parts: Arrange, Act and Assert.&lt;/p&gt;

&lt;p&gt;In the Arrange part, we create classes and input values needed to call the entry point of the code under test.&lt;/p&gt;

&lt;p&gt;In the Act part, we call the method to trigger the logic being tested.&lt;/p&gt;

&lt;p&gt;In the Assert part, we verify the code under test did what we expected. We check if it returned the right value, threw an exception, or called another component.&lt;/p&gt;

&lt;p&gt;For example, let’s bring back one test for &lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;Stringie&lt;/a&gt;, a (fictional) library to manipulate strings, to show the AAA pattern. Notice how each test has these 3 parts.&lt;/p&gt;

&lt;p&gt;For the sake of the example, we have put comments in each AAA part. You don’t need to do that on your own tests.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie.UnitTests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Arrange&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Act&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Assert&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_NoParameters_ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Arrange&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Arrange&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Arrange&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;ICYMI, we’ve been using Stringie to &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;write our first unit tests in C#&lt;/a&gt; and to learn about &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing tests&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-separate-each-a-of-the-aaa-pattern&quot;&gt;2. Separate each A of the AAA pattern&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use line breaks to visually separate the AAA parts of each test.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a look at the previous tests without line breaks between each AAA part.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie.UnitTests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_NoParameters_ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not that bad. The larger the tests, the harder it gets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have the three AAA parts separated to make your tests easier to read.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In case you’re wondering about those weird method names, they follow one of the most common &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;test naming conventions&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1613083093144-bfa5c3eb8337?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjIzNjkzMzU2&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Three paths on the snow&quot; /&gt;

&lt;figcaption&gt;Separate each A of the AAA pattern. Photo by &lt;a href=&quot;https://unsplash.com/@polarmermaid?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Anne Nygård&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-dont-put-logic-in-your-assertions&quot;&gt;3. Don’t put logic in your assertions&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Don’t repeat the logic under test in your assertions.&lt;/strong&gt; And, please, don’t copy the tested logic and paste it into private methods in your test files to use it in your assertions. That’s &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;the most common mistake when writing tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Use known, pre-calculated values instead. Declare constants for common expected values.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world! Again, Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, world! Again,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Notice how we hardcode an expected value here&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how we hardcoded an expected value in the Assert part. We didn’t use any other method or copy the logic under test to find the expected substring.&lt;/p&gt;

&lt;h2 id=&quot;4-have-a-single-act-and-assert&quot;&gt;4. Have a single Act and Assert&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Have a single Act and Assert parts in your tests&lt;/strong&gt;. Use parameterized tests to test the same scenario with different test values.&lt;/p&gt;

&lt;p&gt;And, don’t put the test values inside an array to loop through it to then assert on each value.&lt;/p&gt;

&lt;p&gt;This is a parameterized test with MSTest.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataTestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HELLO&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HeLlo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_SubstringWithDifferentCase_RemovesSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;substringToRemove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substringToRemove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IgnoringCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-use-the-right-assertion-methods&quot;&gt;5. Use the right Assertion methods&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use the right assertion methods of your testing framework&lt;/strong&gt;. And, don’t roll your own assertion framework.&lt;/p&gt;

&lt;p&gt;For example, prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert.IsNull(result);&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert.AreEqual(null, result);&lt;/code&gt;. And, prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert.IsTrue(result)&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert.AreEqual(true, result);&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When working with strings, prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StringAssert&lt;/code&gt; methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Contains()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StartsWith()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Matches()&lt;/code&gt; instead of exactly comparing two strings. That would make your tests easier to maintain.&lt;/p&gt;

&lt;p&gt;Voilà! These are 5 tips to write better assertions. If you want a more complete list of best practices to write your unit tests, check my post &lt;a href=&quot;/2021/07/05/UnitTestingBestPractices/&quot;&gt;Unit Testing Best Practices: A Checklist&lt;/a&gt;. And, don’t miss &lt;a href=&quot;/2021/08/16/WriteCustomAssertions/&quot;&gt;how to write custom assertions&lt;/a&gt; to write even more readable tests.&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, start reading &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt; and &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common unit testing mistakes&lt;/a&gt;. For more advanced tips, check &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;how to write good unit tests&lt;/a&gt; and &lt;a href=&quot;/2021/02/05/FailingTest/&quot;&gt;always write failing tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unit Testing Best Practices: A checklist</title>
   <link href="https://canro91.github.io/2021/07/05/UnitTestingBestPractices/"/>
   <updated>2021-07-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/07/05/UnitTestingBestPractices</id>
   <content type="html">&lt;p&gt;As part of this series on unit testing, we’ve covered a lot of subjects. From &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;how to write your first unit tests&lt;/a&gt; to &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;create test data with Builders&lt;/a&gt; to &lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;how to write better fakes&lt;/a&gt;. I hope I’ve helped you to start writing unit tests or write even better unit tests.&lt;/p&gt;

&lt;p&gt;This time, I’m bringing some tips and best practices from my previous posts in one single place for quick reference.&lt;/p&gt;

&lt;h2 id=&quot;1-on-naming&quot;&gt;1. On Naming&lt;/h2&gt;

&lt;h3 id=&quot;choose-a-naming-convention-and-stick-to-it&quot;&gt;Choose a naming convention and stick to it.&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Every test name should tell the scenario under test and the expected result&lt;/strong&gt;. &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;Don’t worry about long test names&lt;/a&gt;. But don’t name your tests: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Test1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Test2&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Describe in your test names what you’re testing in a language easy to understand, even for non-programmers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t prefix your test names with “Test.”&lt;/strong&gt; If you’re using a testing framework that doesn’t need keywords in your test names, don’t do that. With MSTest, there are attributes like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestClass]&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestMethod]&lt;/code&gt; to mark methods as tests. Other testing frameworks have similar ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t use filler words like “Success” or “IsCorrect” in test names&lt;/strong&gt;. Instead, tell what “success” and “correct” mean for that test. Is it a successful test because it doesn’t throw exceptions? Is it successful because it returns a value different from null? Make your test names easy to understand.&lt;/p&gt;

&lt;h2 id=&quot;2-on-organization&quot;&gt;2. On Organization&lt;/h2&gt;

&lt;h3 id=&quot;make-your-tests-easy-to-find&quot;&gt;Make your tests easy to find.&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Put your unit tests in a test project named after the project they test.&lt;/strong&gt; Use the suffix “Tests” or “UnitTests.” For example, if you have a library called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyLibrary&lt;/code&gt;, name your test project: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyLibrary.UnitTests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put your unit tests separated in files named after the unit of work or entry point of the code you’re testing.&lt;/strong&gt; Use the suffix “Tests”. For a class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyClass&lt;/code&gt;, name your test file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyClassTests&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1426927308491-6380b6a9936f?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjIxNTY2NDk2&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Workbench full of tools&quot; /&gt;

&lt;figcaption&gt;Keep your tests organized and easy to find. Photo by &lt;a href=&quot;https://unsplash.com/@barnimages?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Barn Images&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/organization?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-on-assertions&quot;&gt;3. On Assertions&lt;/h2&gt;

&lt;h3 id=&quot;follow-the-arrangeactassert-aaa-principle&quot;&gt;Follow the Arrange/Act/Assert (AAA) principle.&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Separate the body of your tests.&lt;/strong&gt; Use line breaks to visually separate the three AAA parts in the body of your tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;Don’t repeat the logic under test in your assertions&lt;/a&gt;.&lt;/strong&gt; And, please, don’t copy the tested logic and paste it into private methods in your test files to use it in your assertions. Use known or pre-calculated values, instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t make private methods public to test them.&lt;/strong&gt; Test private methods when calling your code under test through its public methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have a single Act and Assert parts in your tests.&lt;/strong&gt; Don’t put test values inside a collection to loop through it and assert on each one. Use parameterized tests to test the same scenario with different test values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the right assertion methods of your testing framework.&lt;/strong&gt; For example, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert.IsNull(result);&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert.AreEqual(null, result);&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer assertion methods for strings&lt;/strong&gt; like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Contains()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StartsWith()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Matches()&lt;/code&gt; instead of exactly comparing two strings.&lt;/p&gt;

&lt;h2 id=&quot;4-on-test-data&quot;&gt;4. On Test Data&lt;/h2&gt;

&lt;h3 id=&quot;keep-the-amount-of-details-at-the-right-level&quot;&gt;Keep the amount of details at the right level&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Give enough details to your readers&lt;/strong&gt;, but not too many to make your tests noisy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use factory methods&lt;/strong&gt; to reduce complex Arrange scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make your scenario under test and test values extremely obvious.&lt;/strong&gt; Don’t make developers decode your tests. Create constants for common test data and expected values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use object mothers to create input test values.&lt;/strong&gt; Have a factory method or property holding a ready-to-use input object. Then, change what you need to match the scenario under test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer Builders to create complex object graphs.&lt;/strong&gt; Object mothers are fine if you don’t have lots of variations of the object being constructed. If that’s the case, use the &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;Builder pattern&lt;/a&gt;. Compose builders to create complex objects in your tests.&lt;/p&gt;

&lt;h2 id=&quot;5-on-stubs-and-mocks&quot;&gt;5. On Stubs and Mocks&lt;/h2&gt;

&lt;h3 id=&quot;write-dumb-fakes&quot;&gt;Write dumb fakes&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use fakes when you depend on external systems you don’t control.&lt;/strong&gt; Check your code makes the right calls  with the right messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid complex logic inside your fakes.&lt;/strong&gt; Don’t add flags to your stubs to return one value or another. Write separate stubs instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;Don’t write assertions for stubs&lt;/a&gt;.&lt;/strong&gt; Assert on the output of your code under test or use mocks. Remember there’s a &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;difference between stubs and mocks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep one mock per test.&lt;/strong&gt; Don’t use multiple mocks per test. Write separate tests instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make tests set their own values for fakes.&lt;/strong&gt; Avoid magic values inside your stubs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use descriptive names in your fakes.&lt;/strong&gt; Name your stubs to indicate the value they return or the exception they throw. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ItemOutOfStockStockService&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! Those are my best practices for writing &lt;del&gt;better&lt;/del&gt; great unit tests. Don’t forget to always start writing failing tests. And make sure they fail for the right reasons. If you don’t follow Test-Driven Development, comment out some of your code under test or change the assertions on purpose to see your tests failing.&lt;/p&gt;

&lt;p&gt;We don’t ship our tests to end users. But it doesn’t mean we shouldn’t care about the quality of our tests. Unit tests got our back when changing our code. They’re our safety net.&lt;/p&gt;

&lt;p&gt;Don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover all these subjects in depth.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Write simpler tests with Type Builders and AutoFixture</title>
   <link href="https://canro91.github.io/2021/06/21/WriteSimplerTestsTypeBuilderAndAutoFixture/"/>
   <updated>2021-06-21T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/06/21/WriteSimplerTestsTypeBuilderAndAutoFixture</id>
   <content type="html">&lt;p&gt;Writing tests for services with lots of collaborators can be tedious. I know! We will end up with complex Arrange parts and lots of fakes. Let’s see three alternatives to write simpler tests with builder methods, Type Builders and AutoFixture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write simpler tests for services with lots of collaborators, use builder methods to create only the fakes needed in every test. As an alternative, use auto-mocking to create a service with its collaborators replaced by test doubles.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To show these three alternatives, let’s bring back our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; class. We used it to show the &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;difference between stubs and mocks&lt;/a&gt;. Again, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; checks if an item has stock available to then charge a credit card.&lt;/p&gt;

&lt;p&gt;This time, let’s add an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDeliveryService&lt;/code&gt; to create a shipment order and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOrderRepository&lt;/code&gt; to keep track of order status. With these two changes, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; will look like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDeliveryService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_deliveryService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IOrderRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_orderRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;IDeliveryService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deliveryService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;IOrderRepository&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_deliveryService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deliveryService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_orderRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrderResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OutOfStockException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Process payment, ship items, and store order status...&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrderResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I know, I know! We could argue our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; is doing a lot of things! Bear with me.&lt;/p&gt;

&lt;p&gt;Let’s write a test to check if the payment gateway is called when we place an order. We’re using &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;Moq to write fakes&lt;/a&gt;. This test will look like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WithoutAnyBuilders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTestsBefore&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()))&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deliveryService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDeliveryService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOrderRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                       &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                       &lt;span class=&quot;n&quot;&gt;deliveryService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                       &lt;span class=&quot;n&quot;&gt;orderRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sometimes, we need to create fakes for our collaborators even when the behavior under test doesn’t need them.&lt;/p&gt;

&lt;h2 id=&quot;1-builder-methods&quot;&gt;1. Builder methods&lt;/h2&gt;

&lt;p&gt;One easy alternative to writing simpler tests is to use builder methods.&lt;/p&gt;

&lt;p&gt;With a builder method, we only create the fakes we need inside our tests. And, inside the builder method, we create “empty” fakes for the collaborators we don’t need for the tested scenario.&lt;/p&gt;

&lt;p&gt;We used this idea of builder methods to &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;write better tests by making our tests less noisy&lt;/a&gt; and more readable.&lt;/p&gt;

&lt;p&gt;Our test with a builder method looks like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WithABuilderMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTestsBuilder&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()))&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeOrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                 ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We add a new MakeOrderService method&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;orderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrderService&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeOrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Notice we only pass the fakes we need&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deliveryService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDeliveryService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOrderRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                        &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                        &lt;span class=&quot;n&quot;&gt;deliveryService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                        &lt;span class=&quot;n&quot;&gt;orderRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MakeOrderService()&lt;/code&gt; method, we only deal with the mocks we care about in our test: the ones for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IStockService&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentService&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1512207736890-6ffed8a84e8d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjIzNjkyODcw&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Men at work&quot; /&gt;

&lt;figcaption&gt;Let&apos;s use builders to write simpler tests. Photo by &lt;a href=&quot;https://unsplash.com/@ripato?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Ricardo Gomez Angel&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-auto-mocking-with-typebuilder&quot;&gt;2. Auto-mocking with TypeBuilder&lt;/h2&gt;

&lt;p&gt;Builder methods are fine. But, we can use a special builder to create testable services with all its collaborators replaced by fakes or test doubles. This way, we don’t need to create builder methods for every combination of services we need inside our tests.&lt;/p&gt;

&lt;p&gt;Let me introduce you to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt;. This is a helper class I’ve been using in one of my client’s projects to create services inside our unit tests.&lt;/p&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; class uses reflection to find all the parameters in the constructor of the service to build. And, it uses &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;Moq to build fakes&lt;/a&gt; for each parameter.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; expects a single constructor. But, we can easily extend it to pick the one with more parameters.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_instances&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ConstructorInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConstructors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ParameterInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ParameterType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MakeGenericType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;ConstructorInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockCtor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetConstructors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockCtor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                    &lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;force&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;force&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WithMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mockExpression&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;mockExpression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_mocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s rewrite our sample test to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; class.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WithTypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTestsTypeBuilder&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 1. Create a builder&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TypeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                    ^^^^^&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// 2. Configure a IStockService fake with Moq&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;typeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//          ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// 3. Build an OrderService instance&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                        ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Retrieve a fake from the builder&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;typeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//          ^^^^&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()));&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is what happened. First, we create a builder with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var typeBuilder = new TypeBuilder&amp;lt;OrderService&amp;gt;();&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, to register a custom fake, we used the method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithMock&amp;lt;T&amp;gt;()&lt;/code&gt;. And inside it, we configured the behavior of the fake.&lt;/p&gt;

&lt;p&gt;In our case, we created a fake &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StockService&lt;/code&gt; that returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; for any order. We did that in these lines:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;typeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After that, with the method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build()&lt;/code&gt; we got an instance of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; class with fakes for all its parameters. But, the fake for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IStockService&lt;/code&gt; has the behavior we added in the previous step.&lt;/p&gt;

&lt;p&gt;Finally, in the Assert part, we retrieved a fake from the builder with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mock&amp;lt;T&amp;gt;()&lt;/code&gt;. We used it to verify if the payment gateway was called or not. We did this here:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;typeBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; class comes in handy to avoid creating builders manually for every service in our unit tests.&lt;/p&gt;

&lt;p&gt;Did you notice in our example that we didn’t have to write fakes for all collaborators? We only did it for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IStockService&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; took care of the other fakes.&lt;/p&gt;

&lt;h2 id=&quot;3-auto-mocking-with-autofixture&quot;&gt;3. Auto-mocking with AutoFixture&lt;/h2&gt;

&lt;p&gt;If you prefer a more battle-tested solution, let’s replace our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; with AutoFixture.&lt;/p&gt;

&lt;h3 id=&quot;what-autofixture-does&quot;&gt;What AutoFixture does&lt;/h3&gt;

&lt;p&gt;From its docs, &lt;a href=&quot;https://autofixture.github.io/&quot;&gt;AutoFixture&lt;/a&gt; &lt;em&gt;“is a tool designed to make Test-Driven Development more productive and unit tests more refactoring-safe”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;AutoFixture creates test data for us. It helps us to simplify the Arrange parts of our tests.&lt;/p&gt;

&lt;p&gt;To start using AutoFixture, let’s install its NuGet package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AutoFixture&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, we can create orders inside our tests with:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Fixture&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fixture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;AutoFixture will initialize all properties of an object to random values. Optionally, we can hardcode our own values if we want to.&lt;/p&gt;

&lt;h3 id=&quot;automoq&quot;&gt;AutoMoq&lt;/h3&gt;

&lt;p&gt;AutoFixture has integrations with mocking libraries like Moq to create services with all its parameters replaced by fakes. To use these integrations, let’s install the NuGet package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AutoFixture.AutoMoq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s rewrite our sample test, this time to use AutoFixture with AutoMoq. It will look like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AutoFixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AutoFixture.AutoMoq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Moq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WithAutoFixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTestsAutoFixture&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 1. Create a field for AutoFixture&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFixture&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fixture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Customize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AutoMoqCustomization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//             ^^^^^&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockedService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Freeze&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 2. Use Freeze to create a custom fake&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stockedService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()))&lt;/span&gt;
                      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Freeze&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fixture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 3. Use Create to grab an auto-mocked instance&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice this time, we used a field in our test to hold a reference to AutoFixture &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fixture&lt;/code&gt; class. Also, we needed to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AutoMoqCustomization&lt;/code&gt; behavior to make AutoFixture a type builder.&lt;/p&gt;

&lt;p&gt;To retrieve a fake reference, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Freeze&amp;lt;T&amp;gt;()&lt;/code&gt; method. We used these references to plug the custom behavior for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IStockService&lt;/code&gt; fake and to verify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentGateway&lt;/code&gt; fake.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how we can use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; helper class and AutoFixture to simplify the Arrange parts of our tests. If you prefer a simple solution, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TypeBuilder&lt;/code&gt; class. But, if you don’t mind adding an external reference to your tests, use AutoFixture. Maybe, you can use it to create test data too.&lt;/p&gt;

&lt;p&gt;If you want to know what fakes and mocks are, check &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;What are fakes in unit testing: mocks vs stubs&lt;/a&gt; and learn these &lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;5 tips to write better stubs and mocks&lt;/a&gt;. And, don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this one.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Five tips for better stubs and mocks in C#</title>
   <link href="https://canro91.github.io/2021/06/07/TipsForBetterStubsAndMocks/"/>
   <updated>2021-06-07T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/06/07/TipsForBetterStubsAndMocks</id>
   <content type="html">&lt;p&gt;Last time, we covered &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;what fakes are in unit testing&lt;/a&gt; and the types of fakes. We wrote two tests for an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; to show the difference between stubs and mocks.&lt;/p&gt;

&lt;p&gt;In case you missed it, fakes are like test “simulators.” They replace external dependencies with testable components. Stubs and mocks are two types of fakes. Stubs are “simulators” that provide values or exceptions. And mocks are “simulators” that record method calls.&lt;/p&gt;

&lt;p&gt;Before we start with the tips, let’s bring back the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; class from our last post.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderService&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrderResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OutOfStockException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrderResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In our last post, we chose certain names for our fakes like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlwaysAvailableStockService&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt;. Let’s see why those names and what to do and not to do when working with fakes.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;TL;DR&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Don’t assert on stubs&lt;/li&gt;
    &lt;li&gt;Keep one mock per test&lt;/li&gt;
    &lt;li&gt;Avoid logic inside your fakes&lt;/li&gt;
    &lt;li&gt;Make tests set their own values for fakes&lt;/li&gt;
    &lt;li&gt;Name your fakes properly&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;1-dont-assert-on-stubs&quot;&gt;1. Don’t assert on stubs&lt;/h2&gt;

&lt;p&gt;Let’s remember that stubs are there to provide values indirectly to our code under test. We make fakes return a value or throw an exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t write assertions for stubs. We don’t need them. Let’s assert on the result of our tests or use mocks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Please, don’t do this.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AlwaysAvailableStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We don&apos;t need this assertion&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this test, let’s notice this assertion,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s redundant. It will never fail because we wrote the fake to always return true. We can get rid of it!&lt;/p&gt;

&lt;p&gt;If we use a &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;mocking library like Moq to write our fakes&lt;/a&gt; and if we forget to set up our stubs, we will get a &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;NullReferenceException&lt;/a&gt;. Our code expects some values that the stubs didn’t provide. With that exception thrown, we will have a failing test.&lt;/p&gt;

&lt;p&gt;If we write assertions for our stubs, we’re testing the mocking library, not our code.&lt;/p&gt;

&lt;h2 id=&quot;2-keep-one-mock-per-test&quot;&gt;2. Keep one mock per test&lt;/h2&gt;

&lt;p&gt;In the same spirit of keeping a single assertion per test, let’s keep one mock per test. Let’s have small and well-named tests.&lt;/p&gt;

&lt;p&gt;Let’s say that in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt;, we need to log every request we made to charge a credit card and we add an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ILogger&amp;lt;AccountService&amp;gt;&lt;/code&gt; to our service.&lt;/p&gt;

&lt;p&gt;Please, don’t write tests with more than one mock. Like this one,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGatewayAndLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakeLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AlwaysAvailableStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Let&apos;s keep one mock per test&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Don’t use multiple mocks per test. Let’s write separate tests, instead.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And, when &lt;a href=&quot;/2022/12/04/TestingLoggingAndLogMessages/&quot;&gt;testing logging and logging messages&lt;/a&gt;, asserting on the logging message isn’t a good idea.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/flagged/photo-1579750481098-8b3a62c9b85d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjE5NzExMzk1&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Cockpit of Airbus A330-200&quot; /&gt;

&lt;figcaption&gt;Stubs and mocks are like test &quot;simulators&quot;. Photo by &lt;a href=&quot;https://unsplash.com/@dallimonti?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Andrés Dallimonti&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-avoid-logic-inside-your-fakes&quot;&gt;3. Avoid logic inside your fakes&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Write dumb fakes. Let’s avoid complex logic inside fakes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, let’s not add flags to our stubs to return one value or another. Let’s write separate fakes, instead.&lt;/p&gt;

&lt;p&gt;Let’s test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; with and without stock. As a counterexample, let’s use a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeStockService&lt;/code&gt; with a flag to signal the two scenarios.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Make the stock service have stock&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FakeStockService&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ItemInStock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemOutOfStock_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Make the stock service have NO stock&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FakeStockService&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ItemInStock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutOfStockException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeStockService&lt;/code&gt; would look like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakeStockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemInStock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemInStock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here our fake service uses a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool&lt;/code&gt;, but it could start getting more complex if we need to support more test scenarios.&lt;/p&gt;

&lt;p&gt;Let’s not use a single fake for both scenarios. Instead, let’s write two separate fakes and make each test use a different one. Let’s name these two fakes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ItemInStockStockService&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ItemOutOfStockStockService&lt;/code&gt;. Inside them, we always return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// One fake for the &quot;in stock&quot; scenario&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ItemInStockStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemOutOfStock_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Another fake for the &quot;out of stock&quot; scenario&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ItemOutOfStockStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutOfStockException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Don’t worry about creating lots of fakes. Fakes are cheap. Any decent IDE can create a class implementing an interface or an abstract class with a few clicks or a single keyboard shortcut.&lt;/p&gt;

&lt;h2 id=&quot;4-make-tests-set-their-own-values-for-fakes&quot;&gt;4. Make tests set their own values for fakes&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Avoid magic values in your stubs. Let’s make tests pass their own values instead of having hard-coded values in stubs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s say that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StockService&lt;/code&gt; returns the units available instead of a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;. Check this test,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_NotEnoughStock_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakeStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Quantity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutOfStockException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Why should it throw? Why is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Quantity = 2&lt;/code&gt; there? Because we buried somewhere in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakeStockService&lt;/code&gt; not enough stock. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakeStockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead, let the test set its own faked value,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_NoEnoughStock_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FakeStockService&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;UnitsAvailable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Quantity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutOfStockException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It makes more sense! There’s only 1 unit available and we’re placing an order for 2 items. Let’s make tests fake their own values.&lt;/p&gt;

&lt;h2 id=&quot;5-name-your-fakes-properly&quot;&gt;5. Name your fakes properly&lt;/h2&gt;

&lt;p&gt;Again for our last tip, let’s talk about names. Naming is hard!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Name stubs to indicate the value they return or the exception they throw.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We named our fake stock provider &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlwaysAvailableStockService&lt;/code&gt; to show it always returns stock available. It obvious from its name what is the return value.&lt;/p&gt;

&lt;p&gt;When we needed two stock providers to test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; without stock, we named our fakes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ItemInStockStockService&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ItemOutOfStockStockService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, do you remember why we named our fake &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt;? No? You can tell it by its name. It returns a fixed date, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; you pass to it.&lt;/p&gt;

&lt;p&gt;Voilà! Those are five tips to write better stubs and mocks. Remember, write dumb fakes. Don’t put too much logic in them. Let the tests fake their own values.&lt;/p&gt;

&lt;p&gt;If you want to start using a mocking library, read my post on &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;how to write fakes with Moq&lt;/a&gt;. If you find yourself writing lots of fakes for a single component, check &lt;a href=&quot;/2021/06/21/WriteSimplerTestsTypeBuilderAndAutoFixture/&quot;&gt;automocking with TypeBuilder and AutoFixture&lt;/a&gt;. And don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What are fakes in unit testing? Mocks vs Stubs</title>
   <link href="https://canro91.github.io/2021/05/24/WhatAreFakesInTesting/"/>
   <updated>2021-05-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/05/24/WhatAreFakesInTesting</id>
   <content type="html">&lt;p&gt;Do you know what are fakes? Are stubs and mocks the same thing? Do you know if you need any of them? Once I made the exact same questions. Let’s see what are fakes in unit testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In unit testing, fakes or test doubles are classes or components that replace external dependencies. Fakes simulate successful or failed scenarios to test the logic around the real dependencies they replace.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The best analogy to understand fakes are flight simulators. With a flight simulator, teachers create flight and environment conditions to train and test their pilot students in controlled scenarios.&lt;/p&gt;

&lt;p&gt;Fakes are like flight simulators. Fakes return values, throw exceptions or record method calls to test the code around it. They create the conditions to test our code in controlled scenarios.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1581089780002-02ddf16f57e4?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjE4MzU0Nzgx&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Female aerospace engineer conducts flight simulator&quot; /&gt;

&lt;figcaption&gt;Fakes are like flight simulators. Photo by &lt;a href=&quot;https://unsplash.com/@thisisengineering?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;ThisisEngineering RAEng&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/flight-simulator?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;an-example-of-fakes&quot;&gt;An example of Fakes&lt;/h2&gt;

&lt;p&gt;Let’s move to an example. In our last post when we wrote &lt;a href=&quot;/2021/05/10/WriteTestsThatUseDateTimeNow/&quot;&gt;tests that use DateTime.Now&lt;/a&gt;, we slightly covered the concept of fakes. In that post, we wrote a validator for credit cards. It looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISystemClock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Rest of code here...&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ISystemClock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SystemClock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISystemClock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt; interface with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Now&lt;/code&gt; property to replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; inside our validator. Then, in the unit tests, instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SystemClock&lt;/code&gt; with the real date and time, we wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt; class to always return the same date and time.&lt;/p&gt;

&lt;p&gt;This is one of the tests where wrote that uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt; class.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well, that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt; is a fake. It replaces the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SystemClock&lt;/code&gt; holding the real date and time with a testable alternative. With that fake in place, we make our tests use any date and time we want instead of the real date and time.&lt;/p&gt;

&lt;p&gt;To be more precise, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt; is a stub. But, let’s find out about stubs and mocks.&lt;/p&gt;

&lt;h2 id=&quot;whats-the-difference-between-mocks-and-stubs&quot;&gt;What’s the difference between Mocks and Stubs?&lt;/h2&gt;

&lt;p&gt;Now that we know what fakes are, let’s see two types of fakes: mocks and stubs. This is the difference between them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Both mocks and stubs are fakes or test doubles. Stubs provide values or exceptions to the code under test and mocks are used to assert that a method was called with the right parameters&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;orderservice-example&quot;&gt;OrderService example&lt;/h3&gt;

&lt;p&gt;To better understand the difference between mocks and stubs, let’s use another example. Let’s process online orders with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; checks if an item has stock available to then charge a credit card. Imagine it uses an online payment processing software and a microservice to find the stock of an item. We use two interfaces, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentGateway&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IStockService&lt;/code&gt;, to represent the two dependencies. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderService&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrderResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OutOfStockException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrderResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; class, we should check two things:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It should throw an exception if the purchased item doesn’t have stock.&lt;/li&gt;
  &lt;li&gt;It should take a payment if the purchased item has enough stock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s write a test for the scenario of an item in stock.&lt;/p&gt;

&lt;h3 id=&quot;fake-for-available-stock&quot;&gt;Fake for available stock&lt;/h3&gt;

&lt;p&gt;First, we need a fake that returns if there’s stock available for any order. Let’s call it: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlwaysAvailableStockService&lt;/code&gt;. It looks like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AlwaysAvailableStockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IStockService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsStockAvailable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As its name implies, it will always return stock for any order we pass. It simply returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;fake-for-payment-gateway&quot;&gt;Fake for payment gateway&lt;/h3&gt;

&lt;p&gt;Second, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; works if it charges a credit card. But, we don’t want to charge a real credit card every time we run our test.&lt;/p&gt;

&lt;p&gt;Let’s use a fake to record if the payment gateway was called or not. Let’s name this fake: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakePaymentGateway&lt;/code&gt;. It looks like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakePaymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentGateway&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessPayment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It has a public field &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WasCalled&lt;/code&gt; we set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; when the method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProcessPayment()&lt;/code&gt; is called. This way we can assert if the payment gateway was called.&lt;/p&gt;

&lt;p&gt;Now that we have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlwaysAvailableStockService&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakePaymentGateway&lt;/code&gt; in place, let’s write the actual test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderServiceTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PlaceOrder_ItemInStock_CallsPaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FakePaymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AlwaysAvailableStockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stockService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WasCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                           ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AlwaysAvailableStockService&lt;/code&gt; fake is there to provide a value for our test. It’s a stub. And, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FakePaymentGateway&lt;/code&gt; is used to assert that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; called the method to charge a credit card. It’s a mock. Actually, we could call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MockPaymentGateway&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Again, stubs provides values and mocks are used to assert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s use fakes in our unit tests when we depend on external systems we don’t control.&lt;/strong&gt; For example, third-party APIs and message queues. Let’s assert the right call were made or the right messages were sent.&lt;/p&gt;

&lt;p&gt;In our test, we used &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;the UnitOfWork_Scenario_ExpectedResult naming convention&lt;/a&gt;. For the expect result part, we used the keyword “Calls”. It shows we expect the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderService&lt;/code&gt; to call a payment gateway to charge credit cards.&lt;/p&gt;

&lt;h2 id=&quot;other-types-of-fakes-dummies-stubs-spies-and-mocks&quot;&gt;Other types of fakes: dummies, stubs, spies and mocks&lt;/h2&gt;

&lt;p&gt;We learned about mocks and stubs. But, there are more types of fakes or doubles.&lt;/p&gt;

&lt;p&gt;The book &lt;a href=&quot;http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html&quot;&gt;xUnit Patterns&lt;/a&gt; presents a broader category of fakes. It uses: dummies, stubs, spies and mocks. Let’s quickly go through them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dummies are used to respect the signature of methods and classes under test&lt;/strong&gt;. A dummy is never called inside the code under test. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; value or a null object, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NullLogger&lt;/code&gt;, in a class constructor are dummies when we’re testing one of the class methods that doesn’t use that parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stubs feed our code under test with indirect input values&lt;/strong&gt;. We use stubs when the real dependencies aren’t available in the test environment or when using one will have side effects. Like charging a credit card. For “xUnit Patterns” stubs are exactly the same as what we described earlier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spies are observation points added to the code under test&lt;/strong&gt;. We use spies to check if the code under test called another component or not, and the parameters it used. According to “xUnit Patterns”, mocks we wrote earlier are actually spies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mocks are testable replacements that check if they were used correctly&lt;/strong&gt;. We use mocks when we know in advanced the parameters the code under test will use. With mocks, we set the expected parameters to be used before calling the code under test. Then, we use a verification method in the mock itself to check if the mock was called with those exact same parameters.&lt;/p&gt;

&lt;p&gt;Let’s not get confused with all these terms. Let’s stick to the types of fakes presented in the book &lt;a href=&quot;/2020/03/06/TheArtOfUnitTestingReview/&quot;&gt;The Art of Unit Testing&lt;/a&gt;. In there, there are only two types of fakes or test doubles: stubs and mocks. Everything else is a fake. Easier!&lt;/p&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting thoughts&lt;/h2&gt;

&lt;p&gt;Voilà! That’s what fakes are in unit testing. Remember, stubs provide values for our tests and mocks assert that calls were made. That’s the difference between them.&lt;/p&gt;

&lt;p&gt;Often, we use the terms fake, stubs and mocks interchangeably. And sometimes we use the term “mocking” to mean the replacement of external components with testable equivalents. But, we have seen there’s a distinction between all these terms.&lt;/p&gt;

&lt;p&gt;If you want to start writing fakes with a mocking library, read my post on &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;how to write fakes with Moq&lt;/a&gt;. Also, check these &lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;tips to write better stubs and mocks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;, &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing your first tests&lt;/a&gt; and &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;4 test naming conventions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this one.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to write tests that use DateTime.Now</title>
   <link href="https://canro91.github.io/2021/05/10/WriteTestsThatUseDateTimeNow/"/>
   <updated>2021-05-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/05/10/WriteTestsThatUseDateTimeNow</id>
   <content type="html">&lt;p&gt;In our last post about using &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;builders to create test data&lt;/a&gt;, we wrote a validator for expired credit cards. We used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; all over the place. Let’s see how to write better unit tests that use the current time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write tests that use DateTime.Now, create a wrapper for DateTime.Now and use a fake or test double with a fixed date. As an alternative, create a setter or an optional constructor to pass a reference date&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s continue where we left off. Last time, we wrote two tests to check if a credit card was expired using &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;the Builder pattern&lt;/a&gt;. These are the tests we wrote at that time.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FluentValidation.TestHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UsingBuilders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//                  ^^^^^&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^                            &lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These two tests rely on the current date and time. Every time we run tests that rely on the current date and time, we will have a different date and time. It means we will have different test values and tests each time we run these tests.&lt;/p&gt;

&lt;p&gt;We want our tests to be deterministic. We learned that from &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; in our tests isn’t a good idea.&lt;/p&gt;

&lt;h2 id=&quot;what-are-seams&quot;&gt;What are seams?&lt;/h2&gt;

&lt;p&gt;To replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; in our tests, we need seams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A seam is a place to introduce testable behavior in our code under test. Two techniques to introduce seams are interfaces to declare dependencies in the constructor of a service and optional setter methods to plug in testable values.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s see these two techniques to replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; in our tests.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/37/tEREUy1vSfuSu8LzTop3_IMG_2538.jpg?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjE3MjMwNDA1&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;A clock alarm&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@sonjalangford?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Sonja Langford&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/time?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;1-use-a-fake-or-test-double-to-replace-datetimenow&quot;&gt;1. Use a fake or test double to replace DateTime.Now&lt;/h2&gt;

&lt;p&gt;To make our tests more reliable, let’s create an abstraction for the current time and make our validator depend on it. Later, we can pass a &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;fake or test double&lt;/a&gt; with a hardcoded date in our tests.&lt;/p&gt;

&lt;p&gt;Let’s create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt; interface and a default implementation. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock &lt;/code&gt; will have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Now&lt;/code&gt; property for the current date and time.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ISystemClock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SystemClock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISystemClock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardValidator&lt;/code&gt; will receive in its constructor a reference to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt;. Then, instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; in our validator, it will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Now&lt;/code&gt; property from the clock.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISystemClock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;systemClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Rest of the code here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, let’s create a testable clock and use it in our tests.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FixedDateClock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISystemClock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we named our fake clock &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FixedDateClock&lt;/code&gt; to show it returns the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; we pass to it.&lt;/p&gt;

&lt;p&gt;Our tests with the testable clock implementation will look like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// This time we&apos;re passing a fake clock implementation&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//                  ^^^^&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//                   ^^^^&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With a testable clock in our tests, we replaced all the references to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; with a fixed date in the past.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;UPDATE (June 2024)&lt;/strong&gt;&lt;/em&gt;: &lt;a href=&quot;/2024/06/10/TestingTimeWithTimeProvider/&quot;&gt;.NET 8.0 introduced TimeProvider&lt;/a&gt;, a new abstraction to use and test time. With this new built-in abstraction, we don’t need to roll our own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISystemClock&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;create-constants-for-common-test-values&quot;&gt;Create constants for common test values&lt;/h3&gt;

&lt;p&gt;To make things cleaner, let’s refactor our tests. Let’s use a builder method and read-only fields for the fixed dates.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// vvvvv&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LastYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LastMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Notice the builder method here&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//                  ^^^^^&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCardValidator&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FixedDateClock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s how we can abstract the current date and time with an interface.&lt;/p&gt;

&lt;h2 id=&quot;2-use-a-parameter-in-a-constructor&quot;&gt;2. Use a parameter in a constructor&lt;/h2&gt;

&lt;p&gt;Now, let’s see the second alternative. To replace the interface from our first example, in the constructor we can pass a delegate returning a reference date. Like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nowSelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;nowSelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Rest of the code here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or, even simpler we can pass a plain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; parameter. Like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                         ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Beep, beep, boop&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Rest of the code here...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s stick to a simple parameter and update our tests.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2021&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LastYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LastMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                                      ^^^^^&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yeap! As simple as that.&lt;/p&gt;

&lt;h3 id=&quot;21-write-an-optional-setter&quot;&gt;2.1. Write an optional setter&lt;/h3&gt;

&lt;p&gt;Another variation on this theme is to create a setter inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardValidator&lt;/code&gt; to pass an optional date. Inside the validator, we should check if the optional date is present to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; or not. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentDateTime&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;When&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how we can write more reliable tests that use the current date and time. You can either create an interface or pass a fixed date.&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt; and &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;4 test naming conventions&lt;/a&gt;. For more advanced tips on unit testing, check my posts on &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;how to write good unit tests&lt;/a&gt; and &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;how to write fakes with Moq&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this one.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to create test data with the Builder pattern</title>
   <link href="https://canro91.github.io/2021/04/26/CreateTestValuesWithBuilders/"/>
   <updated>2021-04-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/04/26/CreateTestValuesWithBuilders</id>
   <content type="html">&lt;p&gt;Last time, we learned &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;how to write good unit tests&lt;/a&gt; by reducing noise inside our tests. We used a factory method to simplify complex setup scenarios in our tests. Let’s use the Builder pattern to create test data for our unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With the Builder pattern, an object creates another object. A builder has methods to change the state of an object and a Build() method to return that object ready to use. Often, the Builder pattern is used to create input data inside unit tests&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;tests-without-builders&quot;&gt;Tests without Builders&lt;/h2&gt;

&lt;p&gt;To see the Builder pattern in action, let’s validate credit cards. We will use the &lt;a href=&quot;https://fluentvalidation.net/&quot;&gt;FluentValidation library&lt;/a&gt; to create a validator class. We want to check if a credit card is expired or not. We can write these tests,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FluentValidation.TestHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;UsingBuilders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CardNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4242424242424242&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Cvv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CardNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4242424242424242&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Cvv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In these tests, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestValidate()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ShouldHaveAnyValidationError()&lt;/code&gt; helper methods from FluentValidation to write more readable assertions.&lt;/p&gt;

&lt;p&gt;In each test, we created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; object and modified one single property for the given scenario. We had duplication and magic values when initializing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;From &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;how to write your first unit tests with MSTest&lt;/a&gt;, we learned our test should be deterministic. We shouldn’t rely on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; on our tests, but let’s keep it for now.&lt;/p&gt;

&lt;h2 id=&quot;what-are-object-mothers&quot;&gt;What are Object mothers?&lt;/h2&gt;

&lt;p&gt;In our tests, we should give enough details to our readers, but not too many details to make our tests noisy. We should keep the details at the right level.&lt;/p&gt;

&lt;p&gt;In our previous tests, we only cared about a credit card expiration year and month. We can abstract the creation of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; objects to avoid repetition.&lt;/p&gt;

&lt;p&gt;One alternative to abstract the creation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; objects is to use an object mother.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An object mother is a factory method or property holding a ready-to-use input object. Each test changes the properties of an object mother to match the scenario under test&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For our example, let’s create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; property with valid defaults and tweak it inside each test.&lt;/p&gt;

&lt;p&gt;Our tests with an object mother for credit cards will look like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Instead of creating a new card object each time,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// we rely on this new CreditCard property&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExpirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExpirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// We have this new property to hold a valid credit card&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                 ^^^^^&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CardNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4242424242424242&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Cvv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; property in our test class and how we updated its values from test to test.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1519645261061-3cee4d216668?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjE3MDYwMTA3&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Lego technic toy truck&quot; /&gt;

&lt;figcaption&gt;Let&apos;s use the Builder pattern. Photo by &lt;a href=&quot;https://unsplash.com/@markusspiske?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Markus Spiske&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;what-are-test-builders&quot;&gt;What are Test Builders?&lt;/h2&gt;

&lt;p&gt;Object mothers are fine if we don’t have lots of variations of the object being constructed. But, since this is a post on Builder pattern, let’s create a Builder for credit cards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Builder is a regular class with two types of methods: a Build() method and one or more chainable WithX() methods.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build()&lt;/code&gt; method returns the object the builder builds.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithX()&lt;/code&gt; methods update one or more properties of the object being built. In this name, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt; refers to the property the method changes.&lt;/p&gt;

&lt;p&gt;These &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithX()&lt;/code&gt; methods return a reference to the builder itself. This way, we can chain many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithX()&lt;/code&gt; methods one after the other. One for each property we want to change.&lt;/p&gt;

&lt;p&gt;For our example, let’s create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardBuilder&lt;/code&gt; with three methods: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithExpirationYear()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithExpirationMonth()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardBuilder&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cardNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cvv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCardBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_expirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCardBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_expirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Other WithX() methods...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CardNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cardNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Cvv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cvv&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In our builder, we have one field for each property of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCard&lt;/code&gt; class. We can create as many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithX()&lt;/code&gt; methods as properties we need to use in our tests.&lt;/p&gt;

&lt;h3 id=&quot;how-to-initialize-values-inside-builders&quot;&gt;How to initialize values inside Builders?&lt;/h3&gt;

&lt;p&gt;To initialize the properties of the object being built, we can create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithTestValues()&lt;/code&gt; method to pass safe defaults or initialize all the fields on the builder directly.&lt;/p&gt;

&lt;p&gt;Let’s stick to the safe defaults out-the-box for our example.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardBuilder&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cardNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4242424242424242&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                            ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                            ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                             ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cvv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                 ^^^&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// All WithX() methods...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreditCard&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CardNumber&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cardNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationYear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ExpirationMonth&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Cvv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cvv&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardBuilder&lt;/code&gt;, let’s update our two sample tests to use it. Notice that when we use the Builder pattern, the last method in the chain of calls is always the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build()&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardValidationTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredYear_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Now, instead of creating cards with the new keyword&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// or using object mothers, we use a builder&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddYears&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCard_ExpiredMonth_ReturnsInvalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardValidator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//                   ^^^^^&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithExpirationMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMonths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TestValidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ShouldHaveAnyValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-to-compose-builders&quot;&gt;How to compose Builders?&lt;/h3&gt;

&lt;p&gt;With the Builder pattern, we can compose many builders to make our tests easier to read.&lt;/p&gt;

&lt;p&gt;To show composition with builders, let’s book a room online. If we use an expired credit card when booking a room, our code will throw an exception. Let’s write a test for that.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BookRoomTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BookRoom_ExpiredCreditCard_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BookingService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BookingRequestBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithGuest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;John Doe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithCreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;c1&quot;&gt;//              ^^^^^&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ExpiredCreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidCreditCardException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BookRoom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice this time, we have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BookingRequestBuilder&lt;/code&gt; to create booking requests. This builder has two methods: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithGuest()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithCreditCard()&lt;/code&gt;. Instead of creating credit cards directly, we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreditCardBuilder&lt;/code&gt; again. We created a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExpiredCreditCard()&lt;/code&gt; method to build expired credit cards.&lt;/p&gt;

&lt;p&gt;We can simplify our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WithCreditCard()&lt;/code&gt; method even further to receive a credit card builder, not a credit card object. Like this.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BookRoomTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BookRoom_ExpiredCreditCard_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BookingService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Notice WithCreditCard() receives a builder this time&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BookingRequestBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithGuest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;John Doe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithCreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreditCardBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                                            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ExpiredCreditCard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                                            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                                            &lt;span class=&quot;c1&quot;&gt;// No extra .Build() here&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvalidCreditCardException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BookRoom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how we can use the Builder pattern to create test data for our unit tests. I hope you have more readable tests using the Builder pattern after reading this post. Remember, in your tests, you should give enough details to your readers, but not too many to make your tests noisy.&lt;/p&gt;

&lt;p&gt;We used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DateTime.Now&lt;/code&gt; in our tests, let’s see &lt;a href=&quot;/2021/05/10/WriteTestsThatUseDateTimeNow/&quot;&gt;how to write tests that use DateTime.Now&lt;/a&gt; in a future post.&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt; to write your first unit tests in C# and learn how to name your test with &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;these four naming conventions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more advanced tips on unit testing, check my post on &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;how to write good unit tests&lt;/a&gt; and &lt;a href=&quot;/2021/02/05/FailingTest/&quot;&gt;always write failing tests&lt;/a&gt;. And don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series for more subjects on unit testing.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to name your unit tests: Four test naming conventions</title>
   <link href="https://canro91.github.io/2021/04/12/UnitTestNamingConventions/"/>
   <updated>2021-04-12T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/04/12/UnitTestNamingConventions</id>
   <content type="html">&lt;p&gt;From our previous post, we learned about &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;four common mistakes&lt;/a&gt; we make when writing our first unit tests. One of them is not to follow a naming convention. Let’s see four naming conventions for our unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test names should tell the scenario under test and the expected result. Writing long names is acceptable since test names should show the purpose behind what they’re testing. When writing tests, prefer good test names over assertion messages.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are four common naming conventions we can use. Let’s continue to use Stringie, a (fictional) library to manipulate strings. Stringie has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method to remove a substring from the beginning or end of an input string.&lt;/p&gt;

&lt;h2 id=&quot;1-unitofwork_scenario_expectedresult&quot;&gt;1. UnitOfWork_Scenario_ExpectedResult&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_NoParameters_ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesOnlyASubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We find this naming convention in the book &lt;a href=&quot;/2020/03/06/TheArtOfUnitTestingReview/&quot;&gt;The Art of Unit Testing&lt;/a&gt;. This convention uses underscores to separate the unit of work or entry point, the test scenario, and the expected behavior.&lt;/p&gt;

&lt;p&gt;With this convention, we can read our test names out loud like this: “When calling Remove with no parameters, then it returns empty.”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’ve included these four naming conventions and more naming tips in my book &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UnitTesting101eBook-Naming&quot;&gt;Start Testing&lt;/a&gt;. A step-by-step guide to writing your first C# unit tests with confidence.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-plain-english-sentence&quot;&gt;2. Plain English sentence&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Returns_empty_with_no_parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Removes_only_a_substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unlike the “UnitOfWork_Scenario_ExpectedResult” convention, this convention strives for a less rigid name structure.&lt;/p&gt;

&lt;p&gt;This convention uses sentences in plain English for test names. We describe in a sentence what we’re testing in a language easy to understand, even for non-programmers. For more readability, we separate each word in our sentence with underscores.&lt;/p&gt;

&lt;p&gt;This convention considers smells adding method names and filler words like “should” or “should be” in our test names. For example, instead of writing, “should_remove_only_a_substring”, we should write “removes_only_a_substring”.&lt;/p&gt;

&lt;p&gt;You could read more about this convention in Vladimir Khorikov’s post: &lt;a href=&quot;https://enterprisecraftsmanship.com/posts/you-naming-tests-wrong/&quot;&gt;You are naming your tests wrong!&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1524411289573-283bf2f298e8?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=500&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjE2NDQ3NzQ3&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;jar of blueberries&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@hudsoncrafted?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Debby Hudson&lt;/a&gt; on &lt;a href=&quot;/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-a-sentence-from-classes-and-methods-names&quot;&gt;3. A sentence from classes and methods names&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveGivenASubstring&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This naming convention uses sentences in plain English too. In this case, class names will act as the subject of our sentences and method names as the verb and the complement. We write units of work or entry points in class names and expected results in method names.&lt;/p&gt;

&lt;p&gt;Also, we can split different scenarios into separate classes. In the class names, we add the keyword &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Given&lt;/code&gt; followed by the scenario under test.&lt;/p&gt;

&lt;p&gt;For our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method, we can name our test class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemoveGivenASubstring&lt;/code&gt; and our test methods &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemovesOnlyASubstring&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemovesSubstringFromTheEnd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this convention, we can read our test names like complete sentences in the “Test Explorer” menu in Visual Studio when we group our tests by class. Like this: “Remove, given a substring, removes that substring.”&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-04-12-UnitTestNamingConventions/TestExplorerGroupByClasses.png&quot; alt=&quot;Visual Studio Solution Explorer with our sample tests&quot; /&gt;
    &lt;figcaption&gt;Visual Studio &apos;Solution Explorer&apos; showing our sample tests group by class&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You can read more about this convention in ardalis’ post: &lt;a href=&quot;https://ardalis.com/unit-test-naming-convention/&quot;&gt;Unit Test Naming Convention&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;4-nested-classes-and-methods&quot;&gt;4. Nested classes and methods&lt;/h2&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GivenASubstring&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This last convention uses sentences split into class and method names too. Unlike the previous naming convention, each scenario has its own nested class.&lt;/p&gt;

&lt;p&gt;For example, instead of having a test class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemoveGivenASubstring&lt;/code&gt;, we create a nested class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GivenASubstring&lt;/code&gt; inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemoveTests&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;You can learn more about this last convention in Kevlin Henney’s presentation &lt;a href=&quot;https://www.youtube.com/watch?v=tWn8RA_DEic&quot;&gt;Structure and Interpretation of Test Cases&lt;/a&gt; on YouTube.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how we can name our unit tests. Remember naming things is hard. Pick one of these four naming conventions and stick to it. But, if you inherit a codebase, prefer the convention already in use. I hope you can write more readable test names after reading this post.&lt;/p&gt;

&lt;p&gt;If you want to practice naming unit tests, check my &lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;Unit Testing 101&lt;/a&gt; repository. There you will find the test names that Stringie developers wrote. &lt;em&gt;Your mission, Jim, should you choose to accept it, is to write better names.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/Testing101.svg&quot; alt=&quot;canro91/Testing101 - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read my post on &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;how to write your first unit tests in C#&lt;/a&gt; and check the &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing your first tests&lt;/a&gt;. Don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I also cover mocking, assertions, and other best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Four common mistakes when writing your first unit tests</title>
   <link href="https://canro91.github.io/2021/03/29/UnitTestingCommonMistakes/"/>
   <updated>2021-03-29T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/03/29/UnitTestingCommonMistakes</id>
   <content type="html">&lt;p&gt;Last time, we covered how to &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;write our first unit tests&lt;/a&gt; with C# and MSTest. We started from a Console program and converted it into our first unit tests. We wrote those tests for Stringie, a (fictional) library to manipulate strings with more readable methods. This time, we will cover how NOT to write unit tests. These are four common mistakes we should avoid when writing our first unit tests.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;TL;DR&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;Do not follow a naming convention&lt;/li&gt;
    &lt;li&gt;Do not use the right assertion methods&lt;/li&gt;
    &lt;li&gt;Do not have a single assertion per test&lt;/li&gt;
    &lt;li&gt;Repeat logic in your assertions&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;mistake-1-do-not-follow-a-naming-convention&quot;&gt;Mistake 1: Do not follow a naming convention&lt;/h2&gt;

&lt;p&gt;First, keep your tests in the right place. Have one test project per project, one test class per class. Add the suffix “Tests” in the name of your test projects and classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose a naming convention for your test names and stick to it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;our previous post&lt;/a&gt;, we covered two naming conventions. An “ItShould” sentence and the “UnitOfWork_Scenario_ExpectedResult”, a three-part name separated with underscores. You can choose the one you like the most.&lt;/p&gt;

&lt;p&gt;That time, for Stringie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method, following the “UnitOfWork_Scenario_ExpectedResult” convention, we wrote test names like these ones:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_NoParameters_ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every test should tell the scenario under test and the expected result. We shouldn’t worry about long test names. But, let’s stop naming our tests: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Test1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Test2&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t prefix our test names with “Test.”&lt;/strong&gt; If we’re using a testing framework that doesn’t need keywords in our test names, let’s stop doing that. With MSTest, we have attributes like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestClass]&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestMethod]&lt;/code&gt; to mark our methods as tests.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;don’t use filler words like “Success” or “IsCorrect” in our test names&lt;/strong&gt;. Instead, let’s tell what “success” and “correct” means for that test. Is it a successful test because it doesn’t throw exceptions? Is it successful because it returns a value? Make your test names easy to understand.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’ve included these mistakes and more lessons in my book &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UnitTesting101eBook-Mistakes&quot;&gt;Start Testing&lt;/a&gt;. A step-by-step guide to writing your first C# unit tests with confidence.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;mistake-2-do-not-use-the-right-assertion-methods&quot;&gt;Mistake 2: Do not use the right assertion methods&lt;/h2&gt;

&lt;p&gt;Follow the &lt;a href=&quot;/2021/07/19/WriteBetterAssertions/&quot;&gt;Arrange/Act/Assert principle&lt;/a&gt;. Separate the body of your tests to differentiate these three parts.&lt;/p&gt;

&lt;p&gt;For the Assert part of your tests, make sure to use an assertion library. MSTest, NUnit, and XUnit are the three most popular ones for C#.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the right assertion methods of your library.&lt;/strong&gt; For example, MSTest has assertion methods for strings, collections, and other objects. For a list of the most common MSTest assertions methods, check the MSTest Cheatsheet in &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please, don’t do.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anotherResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Prefer.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsTrue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anotherResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1521978562062-4a694d7d0e74?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjE1MzI0NDE5&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Unit Testing 101&quot; /&gt;

&lt;figcaption&gt;Arrggg! Photo by &lt;a href=&quot;https://unsplash.com/@steve_j?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Steve Johnson&lt;/a&gt; on &lt;a href=&quot;/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;mistake-3-do-not-have-a-single-assertion-per-test&quot;&gt;Mistake 3: Do not have a single assertion per test&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Have only one Act and Assert part in your tests.&lt;/strong&gt; Don’t repeat the same Act part with different test values in a single test.&lt;/p&gt;

&lt;p&gt;Please, avoid writing tests like this one.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_SubstringWithDifferentCase_RemovesSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IgnoringCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HELLO&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IgnoringCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HeLlO&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IgnoringCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we tested the same method with different test values in a single test.&lt;/p&gt;

&lt;p&gt;Also, avoid writing tests like this one.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_SubstringWithDifferentCase_RemovesSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testCases&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;HELLO&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;HeLlO&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testCases&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IgnoringCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, to avoid repetition, we put the test values in an array and looped through them to test each value.&lt;/p&gt;

&lt;p&gt;If we want to test the same scenario with different test values, let’s use parameterized tests instead.&lt;/p&gt;

&lt;h3 id=&quot;how-to-write-parameterized-tests-with-mstest&quot;&gt;How to write Parameterized tests with MSTest&lt;/h3&gt;

&lt;p&gt;To write a parameterized test with MSTest, we can follow these steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestMethod]&lt;/code&gt; attribute with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[DataTestMethod]&lt;/code&gt; attribute in your test.&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[DataRow]&lt;/code&gt; attributes for each set of test values.&lt;/li&gt;
  &lt;li&gt;Add parameters for each test value inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[DataRow]&lt;/code&gt; attributes.&lt;/li&gt;
  &lt;li&gt;Use the input parameters in your test to arrange, act or assert.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s convert the previous test with repeated test values into a parameterized test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataTestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HELLO&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HeLlo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_SubstringWithDifferentCase_RemovesSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;substringToRemove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RemoveAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;substringToRemove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IgnoringCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With parameterized tests, we have separate tests. Inside Visual Studio, in the “Test Explorer” menu, we will have one result per each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[DataRow]&lt;/code&gt; attribute in the parent test.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-03-29-UnitTestingCommonMistakes/TestExplorerDetailSummary.png&quot; alt=&quot;Visual Studio &apos;Test Explorer&apos; showing the result outcomes for our parameterized test&quot; /&gt;
    &lt;figcaption&gt;Visual Studio &apos;Test Explorer&apos; showing the result outcomes for our parameterized test&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It’s easier to troubleshoot parameterized tests when our tests fail for a single test value.&lt;/p&gt;

&lt;h2 id=&quot;mistake-4-repeat-logic-in-your-assertions&quot;&gt;Mistake 4: Repeat logic in your assertions&lt;/h2&gt;

&lt;p&gt;I can’t stress this enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t repeat the logic under test in your assertions. Use known, hard-coded, pre-calculated values instead.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We shouldn’t copy the tested logic and paste it into a private method in our tests to use it in our assertions. We will have code and bugs in two places.&lt;/p&gt;

&lt;p&gt;Please, don’t write assertions like the one in this test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IndexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For this test, instead of using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Substring()&lt;/code&gt; method to remove the input string, use a known expected value. Write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert.AreEqual(&quot;Hello,&quot;, transformed)&lt;/code&gt;. For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstringFromTheEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;The&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Let&apos;s use a known value in our assertions&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! These are four common mistakes when writing our first unit tests. Remember to put your test in the right places following a naming convention. Also, keep one assertion per test, and don’t repeat logic in your assertions. You will have better tests by avoiding these mistakes.&lt;/p&gt;

&lt;p&gt;If you want to practice identifying and fixing these mistakes, check my &lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;Unit Testing 101&lt;/a&gt; repository. You will find the tests that Stringie developers wrote and some other &lt;del&gt;misatkes&lt;/del&gt; mistakes they made. &lt;em&gt;Your mission, Jim, should you choose to accept it, is to fix them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/Testing101.svg&quot; alt=&quot;canro91/Testing101 - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read my post &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt; to write your first unit tests in C#. Take a closer look at that last mistake on my post &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;Don’t duplicate logic in your Asserts&lt;/a&gt;. For more advanced tips on writing unit tests, check my post &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;how to write good unit tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt; series for more subjects on unit testing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unit Testing 101: Write your first unit test in C# with MSTest</title>
   <link href="https://canro91.github.io/2021/03/15/UnitTesting101/"/>
   <updated>2021-03-15T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/03/15/UnitTesting101</id>
   <content type="html">&lt;p&gt;Do you want to start writing unit tests and you don’t know how to start? Were you asked to write some unit tests on a past interview? Let’s see what a unit test is and how to write your first unit tests in C#.&lt;/p&gt;

&lt;h2 id=&quot;1-what-is-a-unit-test&quot;&gt;1. What is a Unit test?&lt;/h2&gt;

&lt;p&gt;The book &lt;a href=&quot;/2020/03/06/TheArtOfUnitTestingReview/&quot;&gt;The Art of Unit Testing&lt;/a&gt; defines a unit test as “an automated piece of code that invokes a unit of work in the system and then checks a single assumption about the behavior of that unit of work.”&lt;/p&gt;

&lt;p&gt;From the previous definition, a unit of work is any logic exposed through public methods. Often, a unit of work returns a value, changes the internals of the system, or makes an external invocation.&lt;/p&gt;

&lt;p&gt;If that definition answers how to test public methods, we might ask: “What about private methods?” Short answer: &lt;a href=&quot;/2024/11/30/TestingPrivateMethods/&quot;&gt;we don’t test private methods&lt;/a&gt;. We test them when we call our code through its public methods.&lt;/p&gt;

&lt;p&gt;In short, a unit test is code that invokes some code under test and verifies a given behavior of that code.&lt;/p&gt;

&lt;h2 id=&quot;2-why-should-we-write-unit-tests&quot;&gt;2. Why should we write unit tests?&lt;/h2&gt;

&lt;p&gt;Have you ever needed to change your code, but you were concerned about breaking something? I’ve been there too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The main reason to write unit tests is to gain confidence. Unit tests allow us to make changes, with confidence that they will work. Unit tests allow change.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unit tests work like a “safety net” to prevent us from breaking things when we add features or change our codebase.&lt;/p&gt;

&lt;p&gt;In addition, unit tests work like living documentation. &lt;strong&gt;The first end-user of our code is our unit tests.&lt;/strong&gt; If we want to know what a library does, we should check its unit tests. Often, we will find non-documented features in the tests.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’ve included an expanded version of this post and more lessons in my book &lt;a href=&quot;https://imcsarag.gumroad.com/l/unittesting101&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UnitTesting101eBook-Intro&quot;&gt;Start Testing&lt;/a&gt;. A step-by-step guide to writing your first C# unit tests with confidence.&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1530096245889-00f7a06d60cd?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Someone driving a car with his seat-belt on&quot; /&gt;

&lt;figcaption&gt;Your unit tests work like a safety net. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@hihow?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Farzanah Rosli&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-what-makes-a-good-unit-test&quot;&gt;3. What makes a good unit test?&lt;/h2&gt;

&lt;p&gt;Now, we know what a unit test is and why we should write them. The next question we need to answer is: “What makes a test a good unit test?” Let’s see what all good unit tests have in common.&lt;/p&gt;

&lt;h3 id=&quot;our-tests-should-run-quickly&quot;&gt;Our tests should run quickly&lt;/h3&gt;

&lt;p&gt;The longer our tests take to run, the less frequently we run them. And, if we don’t run our tests often, we have doors opened to bugs.&lt;/p&gt;

&lt;h3 id=&quot;our-tests-should-run-in-any-order&quot;&gt;Our tests should run in any order&lt;/h3&gt;

&lt;p&gt;Tests shouldn’t depend on the output of previous tests to run. A test should create its own state and not rely upon the state of other tests.&lt;/p&gt;

&lt;h3 id=&quot;our-tests-should-be-deterministic&quot;&gt;Our tests should be deterministic&lt;/h3&gt;

&lt;p&gt;No matter how many times we run our tests, they should either fail or pass every time. We don’t want our test to use random input, for example.&lt;/p&gt;

&lt;h3 id=&quot;our-tests-should-validate-themselves&quot;&gt;Our tests should validate themselves&lt;/h3&gt;

&lt;p&gt;We shouldn’t debug our tests to make sure they passed or failed. Each test should determine the success or failure of the tested behavior. Let’s imagine we have hundreds of tests, and to make sure they pass, we have to debug every one of them. What’s the point, then?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“It could be considered unprofessional to write code without tests” - Robert Martin, The Clean Coder&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;4-lets-write-our-first-unit-test-with-mstest&quot;&gt;4. Let’s write our first unit test with MSTest&lt;/h2&gt;

&lt;p&gt;Let’s write some unit tests for Stringie, a (fictional) library to manipulate strings with more readable methods.&lt;/p&gt;

&lt;p&gt;One of Stringie methods is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt;.  It removes chunks of text from a string. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; receives a substring to remove. Otherwise, it returns an empty string if we don’t pass any parameters.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;, world!&quot;&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the implementation of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method for the scenario without parameters.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveExtensions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoveString&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemoveString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveString&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RemoveString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoveString&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removeString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removeString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s write some tests for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method. We can write a Console program to test these two scenarios.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TestProject&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helloRemoved&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;helloRemoved&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Remove Hello OK&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Remove Hello failed. Expected: &apos;, world!&apos;. But it was: &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;helloRemoved&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Remove: OK&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Remove failed. Expected: &apos;&apos;. But it was: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, these aren’t real unit tests. They run quickly, but they don’t run in any order and they don’t validate themselves.&lt;/p&gt;

&lt;h3 id=&quot;where-should-we-put-our-tests&quot;&gt;Where should we put our tests?&lt;/h3&gt;

&lt;p&gt;If you’re wondering if &lt;a href=&quot;/2025/02/06/UnitTestingFirstStep/&quot;&gt;our tests are compiled into our Release code&lt;/a&gt;, we don’t ship our tests to our end users in our Release code. Tests live on different projects, separated from our production code.&lt;/p&gt;

&lt;p&gt;Let’s create a new project for our tests.&lt;/p&gt;

&lt;p&gt;Let’s add to the solution containing Stringie a new project of type “MSTest Test Project (.NET Core)”. Since we’re adding tests for the Stringie project, let’s name our new test project Stringie.UnitTests.&lt;/p&gt;

&lt;p&gt;It’s my recommendation to put our unit tests in a test project named after the project they test. We can add the suffix “Tests” or “UnitTests”. For example, if we have a library called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyLibrary&lt;/code&gt;, we should name our test project: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyLibrary.UnitTests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our new test project, let’s add a reference to the Stringie project.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-03-15-UnitTesting101/SolutionExplorer.png&quot; alt=&quot;Visual Studio &apos;Solution Explorer&apos; showing a new file &apos;UnitTest1.cs&apos;&quot; /&gt;
    &lt;figcaption&gt;Visual Studio Solution Explorer with our new test project&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After adding the new test project, Visual Studio created a file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnitTest1.cs&lt;/code&gt;. Let’s rename it! We are adding tests for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method, let’s name this file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemoveTests.cs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One way of making our tests easy to find and group is to separate them  in files named after the unit of work or entry point of the code we’re testing. Let’s add the suffix “Tests”. For a class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyClass&lt;/code&gt;, let’s name our file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyClassTests&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;mstest&quot;&gt;MSTest&lt;/h3&gt;

&lt;p&gt;Now, let’s see what’s inside our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RemoveTests.cs&lt;/code&gt; file.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie.UnitTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestMethod1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It contains one normal class and method. However, they’re annotated with two unusual attributes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestClass]&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestMethod]&lt;/code&gt;. These attributes tell Visual Studio that our file contains unit tests to run.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestClass&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestMethod&lt;/code&gt; attributes belong to a project called MSTest. &lt;a href=&quot;https://github.com/microsoft/testfx&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Microsoft Test Framework (MSTest)&lt;/a&gt; is an open-source unit testing framework. MSTest comes installed with Visual Studio.&lt;/p&gt;

&lt;p&gt;Unit testing frameworks help us to write and run unit tests. Also, they create reports with the results of our tests. Other common unit testing frameworks include &lt;a href=&quot;https://nunit.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NUnit&lt;/a&gt; and &lt;a href=&quot;https://xunit.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;XUnit&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;how-should-we-name-our-tests&quot;&gt;How should we name our tests?&lt;/h3&gt;

&lt;p&gt;Let’s replace the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestMethod1&lt;/code&gt; with a name that follows a naming convention.&lt;/p&gt;

&lt;p&gt;We should &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;use naming conventions&lt;/a&gt; to show the feature tested and the purpose behind of our tests. Test names should tell what they’re testing.&lt;/p&gt;

&lt;p&gt;A name like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestMethod1&lt;/code&gt; doesn’t say anything about the code under test and the expected result.&lt;/p&gt;

&lt;h4 id=&quot;itshould&quot;&gt;ItShould&lt;/h4&gt;

&lt;p&gt;One naming convention for our test names uses a sentence to tell what they’re testing. Often, these names start with the prefix “ItShould” followed by an action. For our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remove()&lt;/code&gt; method, it could be:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ItShouldRemoveASubstring&lt;/li&gt;
  &lt;li&gt;ItShouldReturnEmpty&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1516384100354-0e0bbc0d2e00?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Markers and labels&quot; /&gt;

&lt;figcaption&gt;Test names should tell what they&apos;re testing. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@jontyson?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Jon Tyson&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/name?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h4 id=&quot;unitofwork_scenario_expectedresult&quot;&gt;UnitOfWork_Scenario_ExpectedResult&lt;/h4&gt;

&lt;p&gt;Another convention uses underscores to separate the unit of work, the test scenario, and the expected behavior in our test names. If we follow this convention for our example tests, we name our tests:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Remove_ASubstring_RemovesThatSubstring&lt;/li&gt;
  &lt;li&gt;Remove_NoParameters_ReturnsEmpty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this convention, we can read our test names out loud like this: “When calling Remove with a substring, then it removes that substring.”&lt;/p&gt;

&lt;p&gt;Following the second naming convention, our tests look like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie.UnitTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_NoParameters_ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These names could look funny at first glance. We should use compact names in our code. However, when writing unit tests, readability is important. Every test should state the scenario under test and the expected result. We shouldn’t worry about long test names.&lt;/p&gt;

&lt;h3 id=&quot;how-should-we-write-our-tests-the-aaa-principle&quot;&gt;How should we write our tests? The AAA Principle&lt;/h3&gt;

&lt;p&gt;Now, let’s write the body of our tests.&lt;/p&gt;

&lt;p&gt;To write our tests, let’s follow the &lt;strong&gt;Arrange/Act/Assert (AAA) principle&lt;/strong&gt;. Each test should contain these three parts.&lt;/p&gt;

&lt;p&gt;In the Arrange part, we create input values to call the entry point of the code under test.&lt;/p&gt;

&lt;p&gt;In the Act part, we call the entry point to trigger the logic being tested.&lt;/p&gt;

&lt;p&gt;In the Assert part, we verify the expected behavior of the code under test.&lt;/p&gt;

&lt;p&gt;Let’s use the AAA principle to replace one of our examples with a real test. Also, let’s use line breaks to visually separate the AAA parts.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Stringie.UnitTests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RemoveTests&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_NoParameters_ReturnsEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; class from MSTest to write the Assert part of our test. This class contains methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AreEqual()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsTrue()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsNull()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AreEqual()&lt;/code&gt; method checks if the result from a test is equal to an expected value. In our test, we used it to verify the length of the transformed string. We expect it to be zero.&lt;/p&gt;

&lt;h3 id=&quot;dont-repeat-logic-in-the-assertions&quot;&gt;Don’t repeat logic in the assertions&lt;/h3&gt;

&lt;p&gt;Let’s use a known value in the Assert part instead of repeating the logic under test in the assertions. It’s OK to hardcode some expected values in our tests. &lt;a href=&quot;/2021/10/11/DontRepeatLogicInAssertions/&quot;&gt;We shouldn’t repeat the logic under test&lt;/a&gt; in our assertions. For example, we can use well-named constants for our expected values.&lt;/p&gt;

&lt;p&gt;Here’s an example of how not to write the Assertion part of our second test.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IndexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how it uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Substring()&lt;/code&gt; method in the Assert part to find the string without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello&lt;/code&gt; substring. A better alternative is to use the expected result in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AreEqual()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Let’s rewrite our last test to use an expected value instead of repeating the logic being tested.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Remove_ASubstring_RemovesThatSubstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Here we use the expected result &quot;, world!&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transformed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Repeating the logic under test is only one of the &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;most common mistakes when writing unit tests&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;5-how-can-we-run-a-test-inside-visual-studio&quot;&gt;5. How can we run a test inside Visual Studio?&lt;/h2&gt;

&lt;p&gt;To run a test, let’s right-click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[TestMethod]&lt;/code&gt; attribute of the test and use “Run Test(s)”. Visual Studio will compile your solution and run the test you clicked on.&lt;/p&gt;

&lt;p&gt;After the test runs, let’s go to the “Test Explorer” menu. There we will find the list of tests. A passed test has a green icon. If we don’t have the “Test Explorer”, we can use the “View” menu in Visual Studio and click “Test Explorer” to display it.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-03-15-UnitTesting101/TestExplorer.png&quot; alt=&quot;Visual Studio &apos;Test Explorer&apos; showing a passing test&quot; /&gt;
    &lt;figcaption&gt;Test Explorer with our first passing test&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;That’s a passing test! Hurray!&lt;/p&gt;

&lt;p&gt;If the result of a test isn’t what was expected, the Assertion methods will throw an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AssertFailedException&lt;/code&gt;. This exception or any other unexpected exception flags a test as failed.&lt;/p&gt;

&lt;h2 id=&quot;6-mstest-cheatsheet&quot;&gt;6. MSTest Cheatsheet&lt;/h2&gt;

&lt;p&gt;These are some of the most common Assertion methods in MSTest.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th&gt;Function&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.AreEqual&lt;/td&gt;
      &lt;td&gt;Check if the expected value is equal to the found value&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.AreNotEqual&lt;/td&gt;
      &lt;td&gt;Check if the expected value isn’t equal to the found value&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.IsTrue&lt;/td&gt;
      &lt;td&gt;Check if the found value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.IsFalse&lt;/td&gt;
      &lt;td&gt;Check if the found value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.IsNull&lt;/td&gt;
      &lt;td&gt;Check if the found value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.IsNotNull&lt;/td&gt;
      &lt;td&gt;Check if the found value isn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.ThrowsException&lt;/td&gt;
      &lt;td&gt;Check if a method throws an exception&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Assert.ThrowsExceptionAsync&lt;/td&gt;
      &lt;td&gt;Check if an async method throws an exception&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;StringAssert.Contains&lt;/td&gt;
      &lt;td&gt;Check if a found string contains a substring&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;StringAssert.Matches&lt;/td&gt;
      &lt;td&gt;Check if a found string matches a regular expression&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;StringAssert.DoesNotMatch&lt;/td&gt;
      &lt;td&gt;Check if a found string doesn’t match a regular expression&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CollectionAssert.AreEquivalent&lt;/td&gt;
      &lt;td&gt;Check if two collections contain the same elements&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CollectionAssert.AreNotEquivalent&lt;/td&gt;
      &lt;td&gt;Check if two collections don’t contain the same elements&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CollectionAssert.Contains&lt;/td&gt;
      &lt;td&gt;Check if a collection contains an element&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CollectionAssert.DoesNotContain&lt;/td&gt;
      &lt;td&gt;Check if a collection doesn’t contain an element&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;7-conclusion&quot;&gt;7. Conclusion&lt;/h2&gt;

&lt;p&gt;Voilà! That’s how you write your first unit tests in C# with MSTest. Don’t forget to follow naming conventions and use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; class when writing unit tests.&lt;/p&gt;

&lt;p&gt;If you want to practice writing more tests for Stringie, check my &lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;Unit Testing 101&lt;/a&gt; repository on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/Testing101&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/Testing101.svg&quot; alt=&quot;canro91/Testing101 - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this repo, you will find two lessons: one to write some unit tests and another to fix some unit tests.&lt;/p&gt;

&lt;p&gt;For more content about unit testing, don’t miss the entire series &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href=&quot;https://exceptionnotfound.net/unit-testing-101-write-your-first-unit-test-in-csharp-with-mstest/&quot;&gt;exceptionnotfound.net&lt;/a&gt; as part of the Guest Writer Program. I’d like to thank Matthew for helping me to edit this post.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>How not to write Dynamic SQL</title>
   <link href="https://canro91.github.io/2021/03/08/HowNotToWriteDynamicSQL/"/>
   <updated>2021-03-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/03/08/HowNotToWriteDynamicSQL</id>
   <content type="html">&lt;p&gt;Last time, I showed you &lt;a href=&quot;/2020/12/03/DebugDynamicSQL/&quot;&gt;three tips to debug your Dynamic SQL&lt;/a&gt;. Let’s take a step back. Let’s see what is a dynamic SQL query and how to use one to rewrite a stored procedure with optional parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic SQL is a string with a query to execute. In a stored procedure with optional parameters, Dynamic SQL is used to build a string containing a query with only the comparisons and clauses for the parameters passed with a non-default value&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;without-dynamic-sql&quot;&gt;Without Dynamic SQL&lt;/h2&gt;

&lt;p&gt;Let’s go back to the stored procedure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.usp_SearchUsers&lt;/code&gt; from our previous post &lt;a href=&quot;/2020/12/03/DebugDynamicSQL/&quot;&gt;on debugging Dynamic SQL queries&lt;/a&gt;. This stored procedure finds StackOverflow users by display name or location or both.&lt;/p&gt;

&lt;p&gt;Without Dynamic SQL, we end up with funny comparisons in the WHERE clause. First, we check if the optional parameters have value. To then, with an OR, add the right comparisons. Everything in a single statement.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usp_SearchUsers&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    
  &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DisplayName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s run our stored procedure searching only by DisplayName and see its execution plan.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-03-08-HowNotToWriteDynamicSQL/NoDynamicSQL.png&quot; alt=&quot;Execution plan of searching users by DisplayName&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Search for only a single user by DisplayName&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice SQL Server had to scan the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DisplayName&lt;/code&gt; index and see the number of rows read.&lt;/p&gt;

&lt;p&gt;Sometimes, we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISNULL()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COALESCE()&lt;/code&gt; functions instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IS NULL&lt;/code&gt;. But, those are variations on the same theme.&lt;/p&gt;

&lt;p&gt;The more optional parameters our stored procedure has, the worse our query gets. SQL Server will scan entire tables or indexes to satify our query.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1548630435-998a2cbbff67?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;How not to write Dynamic SQL&quot; /&gt;

&lt;figcaption&gt;&lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@nadineshaabana?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Nadine Shaabana&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/HBABoZYH0yI?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;with-dynamic-sql-the-wrong-way&quot;&gt;With Dynamic SQL, the wrong way&lt;/h2&gt;

&lt;p&gt;Probably, we hear about Dynamic SQL somewhere on the Internet and we decide to use it.&lt;/p&gt;

&lt;p&gt;Then, we write the next version of our stored procedure. Something like the one below.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usp_SearchUsersWithWrongDynamicSQL&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
 
  &lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
  &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SELECT TOP 100 *
  FROM dbo.Users u
  WHERE (@SearchDisplayName IS NULL OR DisplayName LIKE @SearchDisplayName)
    AND (@SearchLocation IS NULL OR Location LIKE @SearchLocation);&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;EXEC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sp_executesql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;@SearchDisplayName NVARCHAR(100), @SearchLocation NVARCHAR(100)&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We moved the exact same query to a string and asked SQL Server to execute that string. That won’t make any difference between the execution plans of both versions. We only put makeup on the problem. &lt;em&gt;Arggg!&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;with-dynamic-sql-the-right-way&quot;&gt;With Dynamic SQL, the right way&lt;/h2&gt;

&lt;p&gt;With Dynamic SQL, we want to create smaller queries for the different set of parameters passed to our stored procedure.&lt;/p&gt;

&lt;p&gt;We need to add only the comparisons and clauses for the parameters passed with non-default values.&lt;/p&gt;

&lt;p&gt;Let’s rewrite the stored procedure to include the conditions to the WHERE based on the parameters passed.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usp_SearchUsers_DynamicSQL&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
 
  &lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
  &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SELECT TOP 100 *
  FROM dbo.Users u
  WHERE 1 = 1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; AND DisplayName LIKE @SearchDisplayName &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; AND Location LIKE @SearchLocation &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;EXEC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sp_executesql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;@SearchDisplayName NVARCHAR(100), @SearchLocation NVARCHAR(100)&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First, we created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@StringToExecute&lt;/code&gt; variable with the first part of the SELECT. We added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1 = 1&lt;/code&gt; on the WHERE to easily add conditions in the next steps.&lt;/p&gt;

&lt;p&gt;Instead of, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1 = 1&lt;/code&gt; we can also use a common or required condition for all other set of parameters.&lt;/p&gt;

&lt;p&gt;Then, notice the two IF statements. We added the conditions to the WHERE clause depending on the parameter passed.&lt;/p&gt;

&lt;p&gt;After that, we executed the query inside the string with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sp_executesql&lt;/code&gt; with the parameter declaration and the parameters themselves.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-03-08-HowNotToWriteDynamicSQL/WithDynamicSQL.png&quot; alt=&quot;Execution plan of searching users by DisplayName&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Search for only a single user by DisplayName with Dynamic SQL&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;With Dynamic SQL, our stored procedure will generate one execution plan for each set of different parameters. That’s the point of using Dynamic SQL.&lt;/p&gt;

&lt;p&gt;This time, SQL Server could seek on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DisplayName&lt;/code&gt; instead of scanning it. That’s better.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how NOT to write a stored procedure with optional parameters with Dynamic SQL. Notice that to make things simple, we didn’t follow all the tips to &lt;a href=&quot;/2020/12/03/DebugDynamicSQL/&quot;&gt;make our Dynamic SQL easier to debug&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re interested in more content about SQL and SQL Server, check my posts on &lt;a href=&quot;/2020/09/28/SQLServerTuningTips/&quot;&gt;Six SQL Server performance tuning tips&lt;/a&gt; and &lt;a href=&quot;/2020/09/30/FormatSQL/&quot;&gt;How to format your SQL queries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to convert 2-digit year to 4-digit year in C#</title>
   <link href="https://canro91.github.io/2021/02/24/ParseTwoDigitYear/"/>
   <updated>2021-02-24T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/02/24/ParseTwoDigitYear</id>
   <content type="html">&lt;p&gt;Today I was working with credit cards and I needed to convert a 2-digit year to a 4-digit one in C#. The first thing that came to my mind was adding 2000 to it. But it didn’t feel right. &lt;em&gt;It wouldn’t be a problem in hundreds of years, though.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To convert 2-digit year into a 4-digit year, use the ToFourDigitYear method inside your current culture’s  calendar.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToFourDigitYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 2021&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, if you’re working with a string containing a date, create a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CultureInfo&lt;/code&gt; instance and set the maximum year to 2099. After that, parse the string holding the date with the custom culture. &lt;em&gt;Et voilà!&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en-US&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TwoDigitYearMax&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2099&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dateString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1 Jan 21&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTimeStyles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// true, 1/1/2021 12:00:00 AM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sources: &lt;a href=&quot;https://stackoverflow.com/questions/2024273/convert-a-two-digit-year-to-a-four-digit-year&quot;&gt;Convert a two digit year&lt;/a&gt;, &lt;a href=&quot;https://www.hanselman.com/blog/how-to-parse-string-dates-with-a-two-digit-year-and-split-on-the-right-century-in-c&quot;&gt;Parse string dates with two digit year&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Visual Studio snippets for Moq</title>
   <link href="https://canro91.github.io/2021/02/22/VisualStudioMoqSnippets/"/>
   <updated>2021-02-22T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/02/22/VisualStudioMoqSnippets</id>
   <content type="html">&lt;p&gt;These days, I use Moq a lot. There are things I like and I don’t like about &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;creating fakes with Moq&lt;/a&gt;. But it’s simple and easy to use.&lt;/p&gt;

&lt;p&gt;I use the same four Moq methods all the time. Setup, ReturnAsync, ThrowsAsync and Verify. That’s all you need. I decided to create snippets inside Visual Studio to avoid typing the same method names every time. These are the snippets I use.&lt;/p&gt;

&lt;h3 id=&quot;create-a-mock-with-moq&quot;&gt;Create a Mock with Moq&lt;/h3&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mn&lt;/code&gt; to create a Mock. It expands to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var mock = new Mock&amp;lt;&amp;gt;();&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/canro91/VSMoqSnippets/master/assets/NewMock.gif&quot; alt=&quot;mn to create a Mock&quot; width=&quot;400&quot; height=&quot;300&quot; /&gt;
&lt;figcaption&gt;Use mn to create a Mock&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;setup-and-return&quot;&gt;Setup and Return&lt;/h3&gt;

&lt;p&gt;With a mock instance, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mr&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Setup&lt;/code&gt; a method to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Return&lt;/code&gt; something.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/canro91/VSMoqSnippets/master/assets/Return.gif&quot; alt=&quot;mr to Setup/Return&quot; width=&quot;400&quot; height=&quot;300&quot; /&gt;
&lt;figcaption&gt;Use mr to Setup/Return&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;setup-and-throwsexception&quot;&gt;Setup and ThrowsException&lt;/h3&gt;

&lt;p&gt;If you want to throw an exception from your mock, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mt&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/canro91/VSMoqSnippets/master/assets/Throw.gif&quot; alt=&quot;mt to Setup/ThrowsException&quot; width=&quot;400&quot; height=&quot;300&quot; /&gt;
&lt;figcaption&gt;Use mt to Setup/ThrowsException&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Also, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mra&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mta&lt;/code&gt; for the asynchronous version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Return&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThrowsException&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;If you want to use the same snippets I use, download the snippets file from &lt;a href=&quot;https://github.com/canro91/VSMoqSnippets&quot;&gt;VSMoqSnippets&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;To load snippets into Visual Studio, from the “Tools” menu, choose “Code Snippets Manager” and import the snippets file.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/VSMoqSnippets&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/VSMoqSnippets.svg&quot; alt=&quot;canro91/VSMoqSnippets - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà! Those are the snippets I use for Moq. Check &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;my Visual Studio setup&lt;/a&gt; for more settings and extensions. Do you want to learn more about fakes? Read &lt;a href=&quot;/2021/05/24/WhatAreFakesInTesting/&quot;&gt;what are fakes in unit testing&lt;/a&gt; and these &lt;a href=&quot;/2021/06/07/TipsForBetterStubsAndMocks/&quot;&gt;tips for better stubs and mocks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Decorator pattern. A real example in C#</title>
   <link href="https://canro91.github.io/2021/02/10/DecoratorPattern/"/>
   <updated>2021-02-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/02/10/DecoratorPattern</id>
   <content type="html">&lt;p&gt;I’ve been working with Stripe to take payments. Depending on the volume of requests you make to the Stripe API, you might exceed the maximum number of requests per second. This is how we can implement a retry mechanism using the Decorator pattern in C#.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Decorator wraps another object to extend its responsabilities, without modifying its existing behavior, while keeping the same signature of public methods. Decorators are used to add orthogonal responsibilities like logging, caching and retrying.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s use Marvel movies to understand the Decorator pattern. When IronMan wore the HULKBUSTER suit in the Age of Ultron, he implemented the Decorator pattern. He had a new functionality, stopping the Hulk, while keeping his same functions, being IronMan. &lt;em&gt;I hope you got it!&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1575676993399-3fa7ed2d7066?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Decorator pattern. A real example in C#&quot; /&gt;

&lt;figcaption&gt;When you wear a jacket, you use the Decorator pattern too. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@xieqizhi?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Archie&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/winter?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;naive-retry-logic&quot;&gt;Naive Retry logic&lt;/h2&gt;

&lt;p&gt;Let’s start with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentService&lt;/code&gt;. This service collects everything it needs to start a payment with Stripe. For example, customer id, fees, destination account, etc. Then, it uses a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentIntentService&lt;/code&gt; to call Stripe API using its C# client.&lt;/p&gt;

&lt;p&gt;This would be the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreatePaymentIntentAsync()&lt;/code&gt; method inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentService&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFeeService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_feeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFeeService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_paymentIntentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_feeService_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntentDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreatePaymentIntentAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PaymentRequestViewModel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currencyCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrencyCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amountInUnits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToMainUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gatewayAccountId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GatewayAccountId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationFee&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_feeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetApplicationFee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddFees&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applicationFee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntentOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentIntentCreateOptions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Amount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amountInUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Currency&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currencyCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ApplicationFeeAmount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applicationFee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Metadata&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Confirm&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;CaptureMethod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;manual&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;OnBehalfOf&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gatewayAccountId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;TransferData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentIntentTransferDataOptions&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Destination&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gatewayAccountId&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentIntentOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRequestOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSuccessfulPaymentIntentDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StripeException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stripeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFailedPaymentIntentDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stripeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We want to retry the method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateAsync()&lt;/code&gt; if it reaches the maximum number of allowed requests by Stripe at a given time.&lt;/p&gt;

&lt;p&gt;We can add retry logic using a helper method. And, wrap the call to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateAsync()&lt;/code&gt; method inside the helper method. Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RetryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentIntentOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRequestOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetSuccessfulPaymentIntentDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StripeException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stripeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetFailedPaymentIntentDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stripeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RetryAsync()&lt;/code&gt; helper method will execute the API call a fixed number of times if it fails with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TooManyRequests&lt;/code&gt; status code. If it fails with a different exception or status code, it propagates the exception to the caller.&lt;/p&gt;

&lt;p&gt;This is a simple implementation of a retry method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RetryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxRetryCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retryCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apiCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StripeException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TooManyRequests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;retryCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retryCountMax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AggregateException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Too many requests&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;retryCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Later, we can replace this helper method with a more robust implementation using &lt;a href=&quot;https://github.com/App-vNext/Polly&quot;&gt;Polly&lt;/a&gt;, for example. It can include incremental delays between failed attempts and timeouts.&lt;/p&gt;

&lt;p&gt;But, using this helper method implies wrapping the methods to retry inside our helper method all over our codebase. Hopefully, if we have a singe place to take payments, that wouldn’t be a problem. Also, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentService&lt;/code&gt; mixes business logic with retry logic. That’s smelly. We should keep responsabilities separated.&lt;/p&gt;

&lt;h2 id=&quot;retry-logic-with-decorator-pattern-lets-decorate&quot;&gt;Retry logic with Decorator pattern: Let’s Decorate&lt;/h2&gt;

&lt;p&gt;For a more clean solution, let’s use the Decorator pattern.&lt;/p&gt;

&lt;p&gt;First, let’s create a decorator called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RetryablePaymentIntentService&lt;/code&gt; for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentIntentService&lt;/code&gt;. Since we want to keep the same API of public methods, the decorator should inherit from the same interface, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentIntentService&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RetryablePaymentIntentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntentCreateOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RequestOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We will fill the details in the next steps&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The decorator will only handle the retry logic. It will use the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentIntentService&lt;/code&gt; to call Stripe. The decorator will receive another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentIntentService&lt;/code&gt; in its constructor.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RetryablePaymentIntentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RetryablePaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_decorated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntentCreateOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RequestOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// We will fill the details in the next steps&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, we named the field in the decorator, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_decorated&lt;/code&gt;. And, yes, the decorator inherits and receives the same type. &lt;em&gt;That’s the trick!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to fill in the details. To complete our decorator, let’s use our previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RetryAsync()&lt;/code&gt; method. Our decorator will look like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RetryablePaymentIntentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentIntentService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentIntentService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RetryablePaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntentService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_decorated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntentCreateOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RequestOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RetryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paymentIntentOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requestOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Same RetryAsync method as before...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, our decorator is ready to use it. In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentService&lt;/code&gt;, we can replace the simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentIntentService&lt;/code&gt; by our new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RetryablePaymentIntentService&lt;/code&gt;. Both services implement the same interface.&lt;/p&gt;

&lt;p&gt;We can create our decorator like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RetryablePaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* Other dependencies */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;inject-decorators-into-aspnet-core-container&quot;&gt;Inject Decorators into ASP.NET Core container&lt;/h2&gt;

&lt;h3 id=&quot;lets-register-our-decorator&quot;&gt;Let’s register our decorator&lt;/h3&gt;

&lt;p&gt;But, if you’re using an ASP.NET Core API project, we can use the dependency container to build the decorator.&lt;/p&gt;

&lt;p&gt;Let’s use an extension method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddPaymentServices()&lt;/code&gt; to group the registration of our services. You can register your services directly into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Startup&lt;/code&gt; class. &lt;em&gt;No problem!&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServiceCollectionExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddPaymentServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddTransient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddTransient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decorated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetRequiredService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RetryablePaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decorated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddTransient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time, we registered the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentIntentService&lt;/code&gt; without specifying an interface. We only used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentIntentService&lt;/code&gt; to register the decorator. When resolved, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PaymentService&lt;/code&gt; will receive the decorator instead of the original service without retry logic.&lt;/p&gt;

&lt;h3 id=&quot;lets-use-scrutor-to-register-our-decorator&quot;&gt;Let’s use Scrutor to register our decorator&lt;/h3&gt;

&lt;p&gt;Optionally, we can use &lt;a href=&quot;https://github.com/khellang/Scrutor&quot;&gt;Scrutor&lt;/a&gt; to register the decorated version of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentIntentService&lt;/code&gt;. Scrutor is a library that adds more features to the built-in dependencies container. Don’t forget to install the Scrutor NuGet package into your project, if you choose this route.&lt;/p&gt;

&lt;p&gt;In that case, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddPaymentServices()&lt;/code&gt; will look like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ServiceCollectionExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddPaymentServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddTransient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// With Scrutor, we need the method Decorate&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decorate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RetryablePaymentIntentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddTransient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, this time we have explicitly register two entries for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IPaymentIntentService&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Decorate()&lt;/code&gt; method does the trick for us.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how we can implement the Decorator pattern to retry API calls. We can also use the decorator pattern to bring logging or caching to our services. Check how you can use the Decorator pattern to &lt;a href=&quot;/2020/06/29/HowToAddACacheLayer/&quot;&gt;add a Redis caching layer with ASP.NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more real-world examples, check my post on &lt;a href=&quot;/2020/12/10/PrimitiveObsession/&quot;&gt;Primitive Obsession&lt;/a&gt;. That’s about handling Stripe currency units. If you want my take on another pattern, check my post about the &lt;a href=&quot;/2020/02/14/PipelinePattern/&quot;&gt;Pipeline pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to write good unit tests: Write failing tests first</title>
   <link href="https://canro91.github.io/2021/02/05/FailingTest/"/>
   <updated>2021-02-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/02/05/FailingTest</id>
   <content type="html">&lt;p&gt;A passing test isn’t always the only thing to look for. It’s important to see our test failing. I learned this lesson the hard way. Let’s see why we should start writing failing tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write reliable unit tests, always start writing a failing test. And make sure it fails for the right reasons. Follow the Red, Green, Refactor principle of Test-Driven Development (TDD). Write a failing test, make it pass, and refactor the code. Don’t skip the failing test part.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-passing-test&quot;&gt;The Passing test&lt;/h2&gt;

&lt;p&gt;Let’s continue with the same example from our previous post on &lt;a href=&quot;/2020/11/02/UnitTestingTips/&quot;&gt;how to write good unit tests by reducing noise and using obvious test values&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From our last example, we had a controller to create, update, and suspend user accounts. Inside its constructor, this controller validated some email addresses from an injected configuration object.&lt;/p&gt;

&lt;p&gt;After we refactored our test from the last post, we ended up with this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController_SenderEmailIsNull_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SenderEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ReplyToEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SupportEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountPersonService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountPersonService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpContextAccessor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IHttpContextAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;accountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;accountPersonService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;emailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;httpContextAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1521925410155-c49b38ec65ac?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Always start writing a failing test&quot; /&gt;

&lt;figcaption&gt;Always start writing a failing test. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@loveneora?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Neora Aylon&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;a-false-positive&quot;&gt;A false positive&lt;/h2&gt;

&lt;p&gt;This time, I had a new requirement. I needed to add a new method to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountController&lt;/code&gt;. This new method &lt;a href=&quot;/2020/08/21/HowToConfigureValues/&quot;&gt;read another configuration object&lt;/a&gt; injected into the controller.&lt;/p&gt;

&lt;p&gt;To follow the convention of &lt;a href=&quot;/2022/12/02/ValidateInputParameters/&quot;&gt;validating required parameters inside constructors&lt;/a&gt;, I also checked for this new configuration object. I wrote a new test and a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MakeAccountController()&lt;/code&gt; builder method to call the constructor with only the parameters I needed.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController_NoNewConfig_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// A new builder method&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;());&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// It calls the constructor with mocks, except for emailConfig and someNewConfig&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the constructor looked like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AccountController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Controller&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;IAccountService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;IAccountPersonService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountPersonService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;IEmailService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpContextAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfiguration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;EmailConfiguration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SenderEmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;SenderEmail&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfiguration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;SomeNewConfig&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;someNewConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;SomeKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// etc...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I ran the test and it passed. Move on! But…Wait! There’s something wrong with that test! Did you spot the issue?&lt;/p&gt;

&lt;h2 id=&quot;make-your-tests-fail-for-the-right-reasons&quot;&gt;Make your tests fail for the right reasons&lt;/h2&gt;

&lt;p&gt;Of course, that test is passing. The code throws an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArgumentNullException&lt;/code&gt;. But, that exception is coming from the wrong place. It comes from the validation for the email configuration, not from our new validation.&lt;/p&gt;

&lt;p&gt;I forgot to use a valid email configuration in the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MakeAccountController()&lt;/code&gt; builder method. I used a mock reference without setting up any values. I only realized that after getting my code reviewed. Point for the code review!&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;());&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Here we need to setup a valid EmailConfiguration&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;someNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Let’s make sure to start always by writing a failing test. And, this test should fail for the right reasons.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we write our tests after writing our production code, let’s comment some parts of our production code to see if our tests fail or change the assertions on purpose.&lt;/p&gt;

&lt;p&gt;When we make a failed test pass, we’re testing the test. We’re making sure it fails and passes when it should. We know we aren’t writing buggy tests or introducing false positives into our test suite.&lt;/p&gt;

&lt;p&gt;A better test for our example would check the exception message. Like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController_NoSomeNewConfig_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; 
        &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;StringAssert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;nameof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SomeNewConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! This task reminded me to see my tests fail for the right reasons. Do you have passing tests? Do they pass and fail when they should? I hope they do after reading this post.&lt;/p&gt;

&lt;p&gt;For more tips on how to write good unit tests, check my follow-up on &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;using simple test values&lt;/a&gt;. Don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I also cover mocking, assertions, and best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy unit testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Livable Code. Living with your mess</title>
   <link href="https://canro91.github.io/2021/01/25/LivableCode/"/>
   <updated>2021-01-25T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/01/25/LivableCode</id>
   <content type="html">&lt;p&gt;These days, I watched a conference online by Sarah Mei (&lt;a href=&quot;https://twitter.com/sarahmei&quot;&gt;@sarahmei&lt;/a&gt;) titled: Livable Code. This is what livable code is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Livable Code is the analogy that building software is like living an space inside a building instead of constructing a house or a building.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building software isn’t like building anything else. It isn’t like planning, constructing and maintaining a tower of apartments. It’s living an space where each decision counts towards having a mess or a enjoyable place to live.&lt;/p&gt;

&lt;h2 id=&quot;code-hoarders&quot;&gt;Code hoarders&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We don’t end up with a messy unlivable code because of a bad choice of frameworks or technologies.&lt;/strong&gt; Of course, the shape of the space will influence the design of our house. But, messy code comes after small decisions made every day. Patch after patch. One day you receive a package and throw the box and the plastic bags in a corner. The next thing you know is that pile almost reaches the ceiling.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1584786454449-ecc77738684c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=480&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=640&quot; alt=&quot;A nice pile of junk&quot; /&gt;

&lt;figcaption&gt;Messy code comes after small decisions made every day. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@lg17?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Lance Grandahl&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;everyday-decisions-and-perfect-clean-code&quot;&gt;Everyday Decisions and Perfect Clean Code&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We have to live with every decision we have made inside our code.&lt;/strong&gt; Instead of, building something and moving on, we live with software. We don’t have “done” projects anymore. We don’t ship software into burned CD’s and hand the maintenance to somebody else. We have to get to live with our code and make it livable for the ones around us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no perfect clean code.&lt;/strong&gt; In fact, it’s unattainable. Perfect clean code only exists on books and blog posts. The same as perfectly design minimalist spaces in magazines. We need certain amount of stuff to live. A pile of books, a video game console. An amount of stuff we can tolerate. We could have spaghetti code that nobody understands or so clean and perfectly crafted code that nobody understands either. Both extremes are unlivable.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/8_UoDmJi7U8?rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;4-rules-for-more-livable-code&quot;&gt;4 rules for more livable code&lt;/h2&gt;

&lt;p&gt;The author in the conference gives 4 rules to make your code more livable.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Don’t make it worse.&lt;/strong&gt; No matter what you have to do, don’t make it worse. Remind this to other team members when needed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improvement over consistency.&lt;/strong&gt; Ship your technical debt away, one change at a time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Inline everyday.&lt;/strong&gt; Don’t have stories for big refactors. Make refactoring an everyday job. &lt;em&gt;Always leave the campground cleaner than you found it.&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Liase.&lt;/strong&gt; Communicate, don’t lie. Treat refactoring as everything you do.
    &lt;ul&gt;
      &lt;li&gt;Don’t ask for permission. But, be upfront.&lt;/li&gt;
      &lt;li&gt;Don’t ask for forgiveness. But, learn every time.&lt;/li&gt;
      &lt;li&gt;Ask for advice. But, don’t always take it.&lt;/li&gt;
      &lt;li&gt;Work together. We all have to live here!&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most important part of software is not the code, or the people. It’s the system.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy code living!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A quick guide to LINQ with examples</title>
   <link href="https://canro91.github.io/2021/01/18/LinqGuide/"/>
   <updated>2021-01-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/01/18/LinqGuide</id>
   <content type="html">&lt;p&gt;Today a friend asked me about LINQ. She was studying for a technical interview. So, dear Alice: This is what LINQ is and these are the most common LINQ methods with examples. All you need to know about LINQ in 15 minutes or less.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Language-Integrated Query (LINQ) is the declarative way of working with collections in C#. LINQ works with databases and XML files too. Apart from extensions methods on the IEnumerable type, LINQ has a query syntax, a SQL-like syntax&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;1-linq-is-declarative&quot;&gt;1. LINQ is declarative&lt;/h2&gt;

&lt;p&gt;It means we write our code stating the results we want instead of doing every step to get those results.&lt;/p&gt;

&lt;p&gt;With LINQ, we write code to &lt;em&gt;“filter a collection based on a condition.”&lt;/em&gt; Instead of writing code to &lt;em&gt;“grab an element, check if it satisfies a condition, then move to the next element, check again…“&lt;/em&gt;, etc.&lt;/p&gt;

&lt;p&gt;LINQ is a better alternative to query collections using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt;, or any other loop. With LINQ, we write more expressive and compact code.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1524985069026-dd778a71c7b4?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=800&quot; alt=&quot;Waiting at a cinema before a movie starts&quot; /&gt;

&lt;figcaption&gt;&lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@ewitsoe?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Erik Witsoe&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-lets-find-our-favorite-movies&quot;&gt;2. Let’s find our favorite movies&lt;/h2&gt;

&lt;p&gt;Let’s start with the collection of movies we have watched. We have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; class with a name, release year, and rating. Let’s find our favorite movies, the ones with a rating greater than 4.5.&lt;/p&gt;

&lt;p&gt;Here’s a Console application that prints our favorite movies,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My favorites:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My favorites:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The Fifth Element: [4.6]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Terminator 2: [4.7]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Avatar: [5]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro: [5]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; loop and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statement to find movies with a rating greater than 4.5. No LINQ so far!&lt;/p&gt;

&lt;p&gt;Also, we used Top-level statements, records and Global usings from &lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;recent C# versions&lt;/a&gt;. That’s why we didn’t write the Main class and import the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Linq&lt;/code&gt; namespace.&lt;/p&gt;

&lt;h2 id=&quot;3-lets-use-our-first-linq-method-where&quot;&gt;3. Let’s use our first LINQ method: Where&lt;/h2&gt;

&lt;p&gt;To work with LINQ, we need to be comfortable with delegates and lambda functions.&lt;/p&gt;

&lt;p&gt;In a few words: a delegate is a pointer to a method. And a lambda function is a method with only the parameters and the body. C# has two built-in delegates: &lt;a href=&quot;/2019/03/22/WhatTheFuncAction/&quot;&gt;Func and Action&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;how-to-filter-a-collection-with-where&quot;&gt;How to filter a collection with Where&lt;/h3&gt;

&lt;p&gt;If we want to filter our list of movies to keep only those with a rating greater than 4.5, the LINQ method to filter collections is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where returns a new collection with only the elements that meet a condition.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s replace our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; loop with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt; method. And let’s use the condition inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statement as the filter condition for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt;. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2009&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                     ^^^^^&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My favorites:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My favorites:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The Fifth Element: [4.6]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Terminator 2: [4.7]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Avatar: [5]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro: [5]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrintMovies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We replaced the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statements with a single line of code:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;More compact, isn’t it? Also, we turned the condition inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statement into a lambda function. This is why we need to be comfortable working with delegates.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt; returned a new collection. It didn’t remove any elements from the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movies&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LINQ methods don’t change the original collection. They return a result without modifying the original one.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;where-accepts-a-method-too&quot;&gt;Where accepts a method too&lt;/h3&gt;

&lt;p&gt;Instead of lambda functions, we can use a method as the filtering condition.&lt;/p&gt;

&lt;p&gt;To replace the lambda function from our previous example, let’s create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsFavorite()&lt;/code&gt; method that receives &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movie&lt;/code&gt; as a parameter and returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool&lt;/code&gt;. For example,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsFavorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsFavorite()&lt;/code&gt; inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt; method to filter our movies. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsFavorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsFavorite()&lt;/code&gt; only has one parameter, we can remove the parameter name, like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsFavorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Way more compact and readable than our original version with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foreach&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;4-most-common-linq-methods&quot;&gt;4. Most common LINQ methods&lt;/h2&gt;

&lt;p&gt;So far, we’ve seen only one LINQ method: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt;. Of course, LINQ has more methods.&lt;/p&gt;

&lt;p&gt;Here are the most common LINQ methods:&lt;/p&gt;

&lt;h3 id=&quot;1-select&quot;&gt;1. Select&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Select transforms a collection by applying a mapping function to every element.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s find only the names of our favorite movies.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;favorites&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The Fifth Element&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Terminator 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Avatar&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time we wrote two nested LINQ methods. For every favorite movie, we only picked only its name. Here, the “mapping” function was the delegate: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movie =&amp;gt; movie.Name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For more readability, we often align the nested LINQ methods vertically by the (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt;) dot.&lt;/p&gt;

&lt;h3 id=&quot;2-any&quot;&gt;2. Any&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Any checks if a collection is empty or has at least one element matching a condition. It doesn’t return a new collection, but either true or false.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s see if we have watched movies with a low rating.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasAnyMovies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// true&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasBadMovies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-all&quot;&gt;3. All&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Unlike Any, All checks if every element inside a collection matches a condition. It also returns either true or false instead of a new collection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s see if we have only watched really-good movies.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weHaveSeenReallyGoodMovies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;All&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-groupby&quot;&gt;4. GroupBy&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;GroupBy groups the elements of a collection based on a key. It returns a collection of “groups”  or “buckets” organized by a key.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s group our movies by rating.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupedByRating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GroupBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupedByRating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Rating: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Output:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Rating: 4.5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Titanic&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Rating: 4.6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// The Fifth Element&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Rating: 4.7&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Terminator 2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Rating: 5&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Avatar&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// My Neighbor Totoro&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Rating: 4&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Platoon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We grouped our list of movies using only one property: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rating&lt;/code&gt;. But, &lt;a href=&quot;/2022/05/30/HowToUseLinqGroupBy/&quot;&gt;GroupBy has other use-cases&lt;/a&gt;: transforming each group and grouping by more than one property.&lt;/p&gt;

&lt;h3 id=&quot;5-first-and-firstordefault&quot;&gt;5. First and FirstOrDefault&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;First and FirstOrDefault return the first element in a collection or the first one matching a condition.&lt;/strong&gt; First throws an exception if the collection is empty or doesn’t have matching elements. And FirstOrDefault returns the default value of the element type, instead.&lt;/p&gt;

&lt;p&gt;Let’s find the oldest film we have watched.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Platoon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we first used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderBy()&lt;/code&gt; to sort the movie collection by release year and then picked the first one.&lt;/p&gt;

&lt;p&gt;In the same spirit of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;First()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault()&lt;/code&gt;, we have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Last()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LastOrDefault()&lt;/code&gt;. They return the last element instead of the first one.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2022/06/27/NET6LinqMethods/&quot;&gt;.NET6 introduced new LINQ methods and oveloads&lt;/a&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault()&lt;/code&gt; and similar &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XOrDefault()&lt;/code&gt; methods have a new overload to pass an optional default value. And, we have methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MinBy()&lt;/code&gt; we can use to replace an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderBy()&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;First()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we &lt;a href=&quot;/2022/07/11/LinqDistinctBySourceCode/&quot;&gt;peek into the source code of DistinctBy&lt;/a&gt;, one of those new LINQ methods, we will see it’s not that intimidating after all.&lt;/p&gt;

&lt;h2 id=&quot;5-cheatsheet&quot;&gt;5. Cheatsheet&lt;/h2&gt;

&lt;p&gt;There are more LINQ methods than the ones we’ve seen so far. These are some of them.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th&gt;Function&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Filter a collection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Transform every element of a collection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Check if a collection is empty&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;All&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Check if every element satisfies a condition&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Count&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Count all elements of a collection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Distinct&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Find the unique elements of a collection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupBy&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group the elements of a collection based on a key&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderBy&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Sort a collection based on a key&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;First&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Find the first element of a collection. Throw if the collection is empty&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;First&lt;/code&gt; but it returns a default value if it’s empty&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Last&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Find the last element of a collection. Throw if the collection is empty&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LastOrDefault&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;It returns a default value if it’s empty, instead&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Single&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Find only one element in a collection matching a condition. Throw, otherwise&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;It returns a default value if there isn’t one matching element, instead&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Take&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Pick the first &lt;em&gt;n&lt;/em&gt; consecutive elements of a collection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TakeWhile&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Pick the first consecutive elements that satisfy a condition&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Skip&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Return a collection without the first &lt;em&gt;n&lt;/em&gt; consecutive elements&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SkipWhile&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Return a collection without the first consecutive elements that satisfy a condition&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sum&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Sum the elements of a collection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Min&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Max&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Find the smallest and largest element of a collection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToDictionary&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Convert a collection into a dictionary&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;That isn’t an exhaustive list. Of course, LINQ has more methods we don’t use often, like &lt;a href=&quot;/2022/07/25/LinqAggregateExplained/&quot;&gt;Aggregate&lt;/a&gt; and &lt;a href=&quot;/2022/08/22/IntersectUnionAndExcept/&quot;&gt;Intersect, Union, and Except&lt;/a&gt;. They’re helpful from time to time.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1523207911345-32501502db22?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MXwxfDB8MXxhbGx8fHx8fHx8fA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=800&quot; alt=&quot;Popcorn&quot; /&gt;

&lt;figcaption&gt;Speaking of movies. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@christianw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Christian Wiediger&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;6-linq-method-syntax-vs-query-syntax&quot;&gt;6. LINQ Method syntax vs Query syntax&lt;/h2&gt;

&lt;p&gt;Up to this point, we have seen LINQ as extension methods on top of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&lt;/code&gt; type. But, LINQ has a language-level query syntax too.&lt;/p&gt;

&lt;p&gt;Let’s find our favorite movies using language-level query syntax this time. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bestOfAll&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It looks like SQL, isn’t it?&lt;/p&gt;

&lt;p&gt;And this is the same code using extension methods,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bestOfAll&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can use any of the two! But let’s favor the syntax used in the codebase we’re working with. If our code uses extension methods on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IEnumerable&lt;/code&gt;, let’s continue to do that.&lt;/p&gt;

&lt;p&gt;But there is one advantage of using query syntax over extension methods: we can create intermediate variables with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; keyword.&lt;/p&gt;

&lt;h3 id=&quot;find-large-files-on-the-desktop-folder&quot;&gt;Find large files on the Desktop folder&lt;/h3&gt;

&lt;p&gt;Let’s find all files inside our Desktop folder larger than 10MB. And, let’s use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; to create a variable. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desktopPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFolderPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpecialFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Desktop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desktop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DirectoryInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;desktopPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;largeFiles&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desktop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                 &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sizeInMb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1024&lt;/span&gt;
                 &lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
                 &lt;span class=&quot;c1&quot;&gt;// We can create intermediate variables with &apos;let&apos;&lt;/span&gt;
                 &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sizeInMb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;
                 &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;largeFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Length&lt;/code&gt; property returns the file size in bytes. We declared an intermediate variable to convert it to megabytes. Like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sizeInMb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s the advantage of using query syntax over extension methods when working with LINQ.&lt;/p&gt;

&lt;h2 id=&quot;7-three-common-linq-mistakes&quot;&gt;7. Three common LINQ mistakes&lt;/h2&gt;

&lt;p&gt;It’s easy to start using LINQ. But, it’s easy to use misuse some of its methods. Here are three common mistakes when using LINQ:&lt;/p&gt;

&lt;h3 id=&quot;1-write-count-instead-of-any&quot;&gt;1. Write Count instead of Any&lt;/h3&gt;

&lt;p&gt;Let’s always prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any()&lt;/code&gt; over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Count()&lt;/code&gt; to check if a collection has elements or an element that meets a condition.&lt;/p&gt;

&lt;p&gt;Let’s do,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-write-where-followed-by-any&quot;&gt;2. Write Where followed by Any&lt;/h3&gt;

&lt;p&gt;Let’s use a condition with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any()&lt;/code&gt; instead of filtering first with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt; to later use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s do,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The same applies to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Where()&lt;/code&gt; method followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Count()&lt;/code&gt;, or any other method that receives a filter condition.&lt;/p&gt;

&lt;h3 id=&quot;3-use-firstordefault-without-null-checking&quot;&gt;3. Use FirstOrDefault without null checking&lt;/h3&gt;

&lt;p&gt;Let’s always check the result when working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LastOrDefault()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleOrDefault()&lt;/code&gt;. If there isn’t one, they will return the default value of the collection type.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// We don&apos;t have movies with a rating lower than 2.0&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1998&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.5f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.6f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Terminator 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4.7f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Avatar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Platoon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1986&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Neighbor Totoro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1988&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;worst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;worst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//                  ^^^^^^^^^^^^ &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// System.NullReferenceException: &apos;Object reference not set to an instance of an object.&apos;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// worst was null.&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For objects, the default value would be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; reference. And do you know what happens when we try to access a property or method on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; reference?… Yes, &lt;a href=&quot;/2023/02/20/WhatNullReferenceExceptionIs/&quot;&gt;it throws NullReferenceException&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To make sure we always have a non-nullable result when working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault()&lt;/code&gt;, let’s use the &lt;a href=&quot;/2020/11/17/DefaultOrEmpty/&quot;&gt;the DefaultIfEmpty method&lt;/a&gt;. It returns a new collection with a default value if the input collection is empty.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefaultIfEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Catwoman&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2004&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                  &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, by mistake, we forget about &lt;a href=&quot;/2022/06/13/LinqMistakes/&quot;&gt;the difference between Single and First and LINQ lazy evaluation&lt;/a&gt; expecting LINQ queries to be cached. These two are more subtle mistakes we make when working with LINQ for the first time.&lt;/p&gt;

&lt;h2 id=&quot;8-conclusion&quot;&gt;8. Conclusion&lt;/h2&gt;

&lt;p&gt;Voilà! That’s it, Alice. That’s all you need to know to start working with LINQ in your code in 15 minutes or less. I know! There are lots of methods. But you will get your back covered with &lt;a href=&quot;/2022/05/16/LINQMethodsInPictures/&quot;&gt;five of the most common LINQ methods&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With LINQ, we write more compact and expressive code. The next time we need to write logic using loops, let’s give LINQ a try!&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy LINQ time!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Three differences between TRUNCATE and DELETE</title>
   <link href="https://canro91.github.io/2021/01/04/TruncateVsDelete/"/>
   <updated>2021-01-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2021/01/04/TruncateVsDelete</id>
   <content type="html">&lt;p&gt;Both DELETE and TRUNCATE remove records from a table. But, these days I learned three differences between TRUNCATE and DELETE statements in SQL Server. Let me share them with you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DELETE accepts a WHERE condition to only remove some records, TRUNCATE doesn’t. DELETE doesn’t reset identity columns to its initial value, but TRUNCATE does. And, DELETE fire triggers, TRUNCATE doesn’t.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To see these three differences in action, let’s create a sample database with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movies&lt;/code&gt; table. It only contains an auto-incremented id, a movie title and an score.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeleteVsTruncate&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeleteVsTruncate&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Score&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Titanic&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;The Fifth Element&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Terminator 2&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;1-truncate-doesnt-accept-a-where-clause&quot;&gt;1. TRUNCATE doesn’t accept a WHERE clause&lt;/h2&gt;

&lt;p&gt;The first difference is about the WHERE clause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DELETE accepts a WHERE clause to only delete some records from a table. But, TRUNCATE doesn’t. It deletes all records from a table.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you try to add a WHERE clause with TRUNCATE, you get &lt;em&gt;“Incorrect syntax near the keyword ‘WHERE’“&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Armageddon&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Armageddon&apos;&lt;/span&gt;
                          &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^^^^^^^&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Incorrect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syntax&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;near&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyword&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;WHERE&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-truncate-resets-identity-columns&quot;&gt;2. TRUNCATE resets Identity columns&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;An identity column is a column with automatic incremented values.&lt;/strong&gt; It’s used to create key values in tables.&lt;/p&gt;

&lt;p&gt;Values for identity columns start from a “seed” value and increase by an “increment” value. You can use any number as seed and any positive or negative number as increment. By default, if you don’t use any seed or increment, it starts from 1 and increments by 1. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDENTITY = IDENTITY(1, 1)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DELETE statements don’t reset identity columns.&lt;/strong&gt; It means new rows will have the next value in the identity columns. But, TRUNCATE does reset identity columns. The next new row will have the seed in the identity column.&lt;/p&gt;

&lt;p&gt;Let’s delete all movies from our sample table and see the Id columns for the new movies.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Avatar&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-01-04-TruncateVsDelete/DeleteDoesNotReset.png&quot; /&gt;
    &lt;figcaption&gt;DELETE doesn&apos;t reset identity columns&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice how ‘Avatar’ still has the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Id = 4&lt;/code&gt; after deleting all movies.&lt;/p&gt;

&lt;p&gt;Now, let’s see how TRUNCATE resets the identity column. This time, let’s use TRUNCATE instead of DELETE.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Platoon&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-01-04-TruncateVsDelete/TruncateResets.png&quot; /&gt;
    &lt;figcaption&gt;TRUNCATE resets identity columns&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice the Id of ‘Platoon’. It’s 1 again. When we created our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Movies&lt;/code&gt; table, we used the default seed and increment.&lt;/p&gt;

&lt;h2 id=&quot;3-truncate-doesnt-fire-triggers&quot;&gt;3. TRUNCATE doesn’t fire Triggers&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A trigger is an special type of store procedure that runs when a given action has happened at the database or table level&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, you can run a custom action inside a trigger after INSERT, DELETE or UPDATE to a table.&lt;/p&gt;

&lt;p&gt;When you work with triggers, you have two virtual tables: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERTED&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETED&lt;/code&gt;. These tables hold the values inserted or deleted in the statement that fired the trigger in the first place.&lt;/p&gt;

&lt;p&gt;Now, back to the differences between TRUNCATE and DELETE. &lt;strong&gt;DELETE fires triggers, TRUNCATE doesn’t.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s create a trigger that shows the deleted values. It uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETED&lt;/code&gt; table.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TRIGGER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrintDeletedMovies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AFTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Deleted Id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Deleted Name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Score&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Deleted Score&apos;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DELETED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, let’s delete our movies with DELETE and TRUNCATE to see what happens. First, let’s add some new movies and let’s use the DELETE.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Titanic&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;The Fifth Element&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Terminator 2&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-01-04-TruncateVsDelete/DeleteFiresTriggers.png&quot; /&gt;
    &lt;figcaption&gt;DELETE fires triggers&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice the results tab with our three sample movies. Now, let’s use TRUNCATE.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Titanic&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;The Fifth Element&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Terminator 2&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-01-04-TruncateVsDelete/TruncateDoesNotFireTriggers.png&quot; /&gt;
    &lt;figcaption&gt;TRUNCATE doesn&apos;t fire triggers&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Now, after truncating the table, we only see in the messages tab the number of rows affected. No movies shown.&lt;/p&gt;

&lt;h2 id=&quot;bonus-rollback-a-truncate&quot;&gt;Bonus: Rollback a TRUNCATE&lt;/h2&gt;

&lt;p&gt;We can rollback a TRUNCATE operation. To see this, let’s add our three movies and use a ROLLBACK with some SELECT’s in between.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Titanic&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;The Fifth Element&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Terminator 2&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRAN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;TRUNCATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ROLLBACK&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2021-01-04-TruncateVsDelete/RollbackTruncates.png&quot; /&gt;
    &lt;figcaption&gt;You can rollback a TRUNCATE&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notice, the two results. The one inside the transaction, before the rollback, is empty. And the last one, after the rollback, with our three movies.&lt;/p&gt;

&lt;p&gt;Voilà! Those are three differences between DELETE and TRUNCATE.&lt;/p&gt;

&lt;p&gt;For more content about SQL Server, check &lt;a href=&quot;/2021/03/08/HowNotToWriteDynamicSQL/&quot;&gt;how to write Dynamic SQL&lt;/a&gt; and &lt;a href=&quot;/2020/09/30/FormatSQL/&quot;&gt;Two free tools to format your SQL queries&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A case of primitive obsession. A real example in C#</title>
   <link href="https://canro91.github.io/2020/12/10/PrimitiveObsession/"/>
   <updated>2020-12-10T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/12/10/PrimitiveObsession</id>
   <content type="html">&lt;p&gt;These days I was working with Stripe API to take payments. And I found a case of primitive obsession. Keep reading to learn how to get rid of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primitive obsession is when developers choose primitive types (strings, integers, decimals) to represent entities of the business domain. To solve this code smell, create classes to model the business entities and to enforce the appropriate business rules.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;using-stripe-api&quot;&gt;Using Stripe API&lt;/h2&gt;

&lt;p&gt;Stripe API uses units to represent amounts. All amounts are multiplied by 100. This is 1USD = 100 units. Also, we can only use amounts between $0.50 USD and $999,999.99 USD. This isn’t the case for all currencies, but let’s keep it simple. For more information, check &lt;a href=&quot;https://stripe.com/docs/currencies#zero-decimal&quot;&gt;Stripe documentation for currencies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The codebase I was working with used two extension methods on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decimal&lt;/code&gt; type to convert between amounts and units. Those two methods were something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToUnits()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToAmount()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But, besides variable names, there wasn’t anything preventing me to use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decimal&lt;/code&gt; instead of Stripe units. It was the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decimal&lt;/code&gt; type for both concepts. Anyone could forget to convert things and charge someone’s credit card more than expected. Arggg!&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1563013544-824ae1b704d3?ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;fm=jpg&amp;amp;crop=entropy&amp;amp;cs=tinysrgb&amp;amp;w=800&amp;amp;h=400&amp;amp;fit=crop&quot; alt=&quot;A case of primitive obsession&quot; /&gt;

&lt;figcaption&gt;&lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@rupixen?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;rupixen.com&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;getting-rid-of-primitive-obsession&quot;&gt;Getting rid of primitive obsession&lt;/h2&gt;

&lt;h3 id=&quot;1-create-a-type-alias&quot;&gt;1. Create a type alias&lt;/h3&gt;

&lt;p&gt;As an alternative to encode units of measure on variable names, we can use a type alias.&lt;/p&gt;

&lt;p&gt;Let’s declare a new type alias with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using Unit = System.Decimal&lt;/code&gt; and change the correct parameters to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt;. But, the compiler won’t warn us if we pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decimal&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt;. See the snippet below.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.VisualStudio.TestTools.UnitTesting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;GettingRidOfPrimitiveObsession&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConvertBetweenAmountAndUnits&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UseTypeAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chargeAmount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;paymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chargeAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;paymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^ It compiles&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentService&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amountToCharge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Magic goes here&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DecimalExtensions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToUnits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using a type alias is more expressive than encoding the unit of measure in variable names and parameters. But, it doesn’t force us to use one type instead of the other.&lt;/p&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt; type alias, we can still, by mistake, pass regular decimals when we meant units.&lt;/p&gt;

&lt;p&gt;Let’s try a better alternative!&lt;/p&gt;

&lt;h3 id=&quot;2-create-a-new-type&quot;&gt;2. Create a new type&lt;/h3&gt;

&lt;p&gt;Now, let’s create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt; class and pass it around instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decimal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the constructor of the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt; class, let’s check if the input amount is inside Stripe bounds. Also, let’s use a method to convert units back to normal amounts.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Unit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;999&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;99&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Amount outside of bounds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice, we made the constructor private and added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromAmount()&lt;/code&gt; factory method for more readability.&lt;/p&gt;

&lt;p&gt;After using a class instead of an alias, the compiler will warn us if we switch the two types by mistake. And, it’s clear from a method signature if it works with amounts or units.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UseAType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chargeAmount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;paymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chargeAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// paymentService.Pay(amount);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ^^^^ cannot convert from &apos;decimal&apos; to &apos;GettingRidOfPrimitiveObsession.Unit&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If needed, we can overload the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; operators to make sure we’re not adding oranges and apples. Decimals and units, I mean.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2021/09/13/TopNewCSharpFeatures/&quot;&gt;Records from C# 9.0&lt;/a&gt; offer a shorter notation for classes to replace primitive values. Records have built-in memberwise comparison, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToString()&lt;/code&gt; methods and copy constructors, among other features.&lt;/p&gt;

&lt;p&gt;We can use custom classes, like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt; class we wrote, to encode restrictions, constraints, and business rules in our business domain. That’s the main takeaway from &lt;a href=&quot;/2021/12/13/DomainModelingMadeFunctional/&quot;&gt;Domain Modeling Made Functional&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how we can get rid of primitive obsession. A type alias was more expressive than encoding units of measure on names. But, a class was a better alternative. By the way, F# supports &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/units-of-measure&quot;&gt;unit of measures&lt;/a&gt; to variables. And, the compiler will warn you if you forget to use the right unit of measure.&lt;/p&gt;

&lt;p&gt;Looking for more content on C#? Check my post series on &lt;a href=&quot;/2019/11/19/TwoCSharpIdioms/&quot;&gt;C# idioms&lt;/a&gt; and my &lt;a href=&quot;/2018/11/17/TheC-DefinitiveGuide/&quot;&gt;C# definitive guide&lt;/a&gt;. Working with Stripe, too? Check my post on how to use the &lt;a href=&quot;/2021/02/10/DecoratorPattern/&quot;&gt;Decorator pattern&lt;/a&gt; to implement retry logic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Always Use a Culture When Parsing Numeric Strings in C#</title>
   <link href="https://canro91.github.io/2020/12/04/UseCultureWhenParsing/"/>
   <updated>2020-12-04T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/12/04/UseCultureWhenParsing</id>
   <content type="html">&lt;p&gt;This week, I reinstalled the operating system of my computer. The new version uses Spanish instead of English. After that, some unit tests started to break in one of my projects. The broken tests verified the formatting of currencies. This is what I learned about parsing numeric strings and unit testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To have a set of always-passing unit tests, use a default culture when parsing numeric strings. Add a default culture to the Parse() and ToString() methods on decimals. As an alternative, wrap each test in a method to change the current culture during its execution&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;failing-to-parse-numeric-strings&quot;&gt;Failing to parse numeric strings&lt;/h2&gt;

&lt;p&gt;Some of the failing tests looked like the one below. These tests verified the separator for each supported currency in the project.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCurrency_IntegerAmount_FormatsAmountWithTwoDecimalPlaces&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToCurrency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And this was the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToCurrency()&lt;/code&gt; method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCurrency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToCurrency()&lt;/code&gt; method didn’t specify any culture. Therefore, it used the user’s current culture. And, the tests expected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; as the separator for decimal places. That wasn’t the case for the culture I started to use after reinstalling my operating system. It was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;,&lt;/code&gt;. That’s why those tests failed.&lt;/p&gt;

&lt;h2 id=&quot;use-a-default-culture-when-parsing&quot;&gt;Use a default culture when parsing&lt;/h2&gt;

&lt;p&gt;To make my failing tests always pass, no matter the culture, I added a default culture when parsing numeric strings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always add a default culture when parsing numeric strings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, you can create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToCurrency()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromCurrency()&lt;/code&gt; methods like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FormattingExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultCulture&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en-US&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCurrency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FromCurrency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that I added a second parameter of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CultureInfo&lt;/code&gt;, which defaults to “en-US.”&lt;/p&gt;

&lt;h2 id=&quot;alternatively-use-a-wrapper-in-your-tests&quot;&gt;Alternatively: Use a wrapper in your tests&lt;/h2&gt;

&lt;p&gt;As an alternative to adding a default culture, I could run each test inside a wrapper that changes the user culture to the one needed and revert it when the test finishes.&lt;/p&gt;

&lt;p&gt;Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RunInCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCulture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentThread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCulture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originalCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, I could refactor the tests to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RunInCulture&lt;/code&gt; wrapper method, like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CultureInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultCulture&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CultureInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en-US&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToCurrency_IntegerAmount_FormatsAmountWithTwoDecimalPlaces&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;RunInCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultCulture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;decimal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToCurrency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AreEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;10.00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s what I learned after reinstalling my computer’s operating system and running some unit tests. I learned to use a default culture in all of my parsing methods. If you change your computer locale, all your tests continue to pass?&lt;/p&gt;

&lt;p&gt;If you’re new to unit testing, read &lt;a href=&quot;/2021/03/15/UnitTesting101/&quot;&gt;Unit Testing 101&lt;/a&gt;, &lt;a href=&quot;/2021/03/29/UnitTestingCommonMistakes/&quot;&gt;4 common mistakes when writing unit tests&lt;/a&gt; and &lt;a href=&quot;/2021/04/12/UnitTestNamingConventions/&quot;&gt;4 test naming conventions&lt;/a&gt;. Don’t miss the rest of my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I also cover mocking, assertions, and best practices.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write readable and maintainable unit tests in C#? Join my course &lt;a href=&quot;https://www.udemy.com/course/mastering-csharp-unit-testing-with-real-world-examples/?referralCode=8456B1B78E2EDE923174&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;UT201Course-Footer&quot; data-goatcounter-title=&quot;UT201 Course: Footer&quot;&gt;Mastering C# Unit Testing with Real-world Examples&lt;/a&gt; on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;All tests turned green!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: Three Tricks to Debug Your Dynamic SQL Queries</title>
   <link href="https://canro91.github.io/2020/12/03/DebugDynamicSQL/"/>
   <updated>2020-12-03T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/12/03/DebugDynamicSQL</id>
   <content type="html">&lt;p&gt;These three tips will help you to troubleshoot your dynamic queries and identify the source of a dynamic query when you find one in your query store or plan cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To make dynamic SQL queries easier to debug, format the generated query with line breaks, add as a comment the name of the source stored procedure and use a parameter to only print the generated query.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;1-format-your-dynamic-sql-queries-for-more-readability&quot;&gt;1. Format your dynamic SQL queries for more readability&lt;/h2&gt;

&lt;p&gt;To read your dynamic queries stored in the plan cache, make sure to insert new lines when appropriate.&lt;/p&gt;

&lt;p&gt;Use a variable for the line endings. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DECLARE @crlf NVARCHAR(2) = NCHAR(13) + NCHAR(10)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, to identify the source of a dynamic query, add as a comment the name of the stored procedure generating it. But, don’t use inside that comment a timestamp or any other dynamic text. Otherwise, you will end up with almost identical entries in the plan cache.&lt;/p&gt;

&lt;h2 id=&quot;2-add-a-parameter-to-print-the-generated-query&quot;&gt;2. Add a parameter to print the generated query&lt;/h2&gt;

&lt;p&gt;To debug the generated dynamic query, add a parameter to print it. And, a second parameter to avoid executing the query.&lt;/p&gt;

&lt;p&gt;For example, you can name these two parameters, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Debug_PrintQuery&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Debug_ExecuteQuery&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;h2 id=&quot;3-change-the-casing-of-variables-and-keywords-inside-your-dynamic-sql&quot;&gt;3. Change the casing of variables and keywords inside your dynamic SQL&lt;/h2&gt;

&lt;p&gt;To distinguish errors between the actual SQL query and the dynamic query, change the casing of keywords and variables inside your dynamic query.&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;In the store procedure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.usp_SearchUsers&lt;/code&gt; below, notice the use of the variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@crlf&lt;/code&gt; to insert line breaks and the comment &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/* usp_SearchUsers */&lt;/code&gt; to identify the source of the query.&lt;/p&gt;

&lt;p&gt;Also, check the two debugging parameters: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Debug_PrintQuery&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Debug_ExecuteQuery&lt;/code&gt;. And, finally, see how the casing is different inside the dynamic SQL.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usp_SearchUsers&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debug_PrintQuery&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TINYINT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debug_ExecuteQuery&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TINYINT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crlf&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
  &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crlf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/* usp_SearchUsers */&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;select * from dbo.Users u where 1 = 1 &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crlf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; and DisplayName like @searchdisplayName &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crlf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; and Location like @searchlocation &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crlf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debug_PrintQuery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PRINT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debug_ExecuteQuery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;EXEC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sp_executesql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StringToExecute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;@searchdisplayName nvarchar(100), @searchlocation nvarchar(100)&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchDisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SearchLocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how you can make your dynamic SQL queries easier to debug. If you’re new to the whole concept of dynamic SQL queries, check &lt;a href=&quot;/2021/03/08/HowNotToWriteDynamicSQL/&quot;&gt;how to NOT to write dynamic SQL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source&lt;/em&gt;: &lt;a href=&quot;https://www.brentozar.com/sql/dynamic/&quot;&gt;Dynamic SQL Pro Tips&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Take Smart Notes. Takeaways</title>
   <link href="https://canro91.github.io/2020/11/18/HowToTakeSmartNotes/"/>
   <updated>2020-11-18T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/11/18/HowToTakeSmartNotes</id>
   <content type="html">&lt;p&gt;“How to Take Smart Notes” describes the Zettelkasten method in depth. It shows how scientists and writers can produce new content from their notes. But, you don’t have to be a scientist to take advantage of this method. Anyone can use it to organize his knowledge.&lt;/p&gt;

&lt;p&gt;The Zettelkasten method is the secret behind Niklas Luhman’s success. He was a prominent German sociologist of the 20th century. He earned the title of Professor at Bielefeld University. To earn this title, he wrote a dissertation based on the notes he had about all the books he had read. He had a collection of over 90.000 notes. Impressive, right?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;TL;DR&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Don’t use notebooks to take notes&lt;/li&gt;
    &lt;li&gt;Don’t organize your notes per subjects and semester&lt;/li&gt;
    &lt;li&gt;Read with pen and paper in hand&lt;/li&gt;
    &lt;li&gt;Write your ideas into cards. Put them in your own words&lt;/li&gt;
    &lt;li&gt;Put an index number on every card&lt;/li&gt;
    &lt;li&gt;Create connections from one card to another&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;what-you-need-to-start-with-zettelkasten&quot;&gt;What you need to start with Zettelkasten&lt;/h2&gt;

&lt;p&gt;To start using the Zettlekasten method, &lt;strong&gt;you only need pen, paper and and slip-box&lt;/strong&gt;. That’s why this method is also called the “slip-box” method.&lt;/p&gt;

&lt;p&gt;All you have to do is have a pen and paper when you read. And, translate what you read to your own words. Don’t copy and paste.&lt;/p&gt;

&lt;p&gt;Alternatively, you can any text editor to use it with your computer. But, don’t complicate things unnecessarily. Good tools should avoid distractions from your main task: &lt;strong&gt;thinking&lt;/strong&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1434030216411-0b793f4b4173?ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;fm=jpg&amp;amp;crop=entropy&amp;amp;cs=tinysrgb&amp;amp;w=800&amp;amp;h=400&amp;amp;fit=crop&amp;amp;ixid=eyJhcHBfaWQiOjF9&quot; alt=&quot;With Zettelkasten, all you need pen and paper when your read&quot; /&gt;

&lt;figcaption&gt;All you need pen and paper when your read. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@craftedbygc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Green Chameleon&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;how-to-start-taking-smart-notes&quot;&gt;How to start taking smart notes&lt;/h2&gt;

&lt;h3 id=&quot;zettelkasten-type-of-notes&quot;&gt;Zettelkasten type of notes&lt;/h3&gt;

&lt;p&gt;The Zettlekasten method uses three types of notes: fleeting, literature and permanent notes.&lt;/p&gt;

&lt;p&gt;Write down everything that comes to your mind on &lt;strong&gt;fleeting notes&lt;/strong&gt;. Once you process these notes, you can toss them.&lt;/p&gt;

&lt;p&gt;While reading, make &lt;strong&gt;literature notes&lt;/strong&gt;. Write down on a card what you don’t want to forget. You should write what the book says on what page. Be selective with your literature notes. Keep your literature notes in a reference system.&lt;/p&gt;

&lt;p&gt;To make &lt;strong&gt;permanent notes&lt;/strong&gt;, review your literature notes and turn them into connections. The goal isn’t to collect, but to generate new ideas and discussions. Ask yourself how it contradicts, expands or challenges your subject of interest.&lt;/p&gt;

&lt;p&gt;Keep a single idea per card. Use a fixed number to identify each card. You can use another card to expand on one. Each note should be self-explanatory.&lt;/p&gt;

&lt;h3 id=&quot;create-a-new-note&quot;&gt;Create a new note&lt;/h3&gt;

&lt;p&gt;To add a note to your slip-box, follow these four steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Does this note relate to another note? Put if after.&lt;/li&gt;
  &lt;li&gt;If that’s not the case, put it at the end.&lt;/li&gt;
  &lt;li&gt;Add links from previous notes to this one or viceversa.&lt;/li&gt;
  &lt;li&gt;Add links to it in index card.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Index cards are notes with references to other notes. They act as entry point to a subject.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1576269483449-3b694997b362?ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;fm=jpg&amp;amp;crop=entropy&amp;amp;cs=tinysrgb&amp;amp;w=800&amp;amp;h=400&amp;amp;fit=crop&amp;amp;ixid=eyJhcHBfaWQiOjF9&quot; alt=&quot;To take smart notes, don&apos;t take notes on notebooks&quot; /&gt;

&lt;figcaption&gt;Don&apos;t take notes on notebooks. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@dimhou?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Dimitri Houtteman&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/notes?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;how-not-to-take-smart-notes&quot;&gt;How not to take smart notes&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Don’t take notes on notebooks and on margins of books&lt;/strong&gt;. These notes end up in different places. You have to remember where you put them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t underline or make margin notes&lt;/strong&gt;. Make a separate note of what got your attention. Put it in the reference system. Then, review it and make it a permanent note.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t store your notes on topics/subject and semester&lt;/strong&gt;. And, don’t store your notes in chronological order either. It doesn’t allow you to reorder notes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t note everything on a notebook&lt;/strong&gt;. Your good ideas will end up entangled with other irrelevant notes. Make sure to use fleeting, literature and permanent notes.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-11-18-HowToTakeSmartNotes/Quote.png&quot; alt=&quot;Read, think and write. Take smart notes along the way&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;why-zettlekasten-method-works&quot;&gt;Why Zettlekasten method works&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reading with pen and paper force you to understand&lt;/strong&gt;. You think you understand something until you have write it in your own words. Make sure you always write the output of your thinking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rereading doesn’t work&lt;/strong&gt;. The next time you read something, you feel familiar. But, it doesn’t mean you understand it. Recalling is what indicates if you have learned something or not. The slip-box will show you your unlearned bits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reviewing doesn’t help for understanding and learning&lt;/strong&gt;. Elaboration is better. It means rewriting what you read in your own words and making connections. The slip-box forces to understand and connect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory is a limited resource&lt;/strong&gt;. Use an external system to remember things. You don’t want to put in your head what you can put on a piece of paper. To get something out of your head, write it down. Use fleeting notes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Read, think and write. Take smart notes along the way”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Voilà! That’s How to Take Smart Notes. Remember, don’t use notebooks or write on book margins. Instead, use indexed cards to take notes and connect them with other cards.&lt;/p&gt;

&lt;p&gt;For more content, read &lt;a href=&quot;/2020/08/29/HowITakeNotes/&quot;&gt;how I use plain text to write notes&lt;/a&gt; and my takeaways from &lt;a href=&quot;/2020/07/14/UltralearningTakeaways/&quot;&gt;Ultralearning&lt;/a&gt; and &lt;a href=&quot;/2020/05/07/PragmaticThinkingAndLearning/&quot;&gt;Pragmatic Thinking and Learning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy note taking!&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>TIL: LINQ DefaultIfEmpty method in C#</title>
   <link href="https://canro91.github.io/2020/11/17/DefaultOrEmpty/"/>
   <updated>2020-11-17T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/11/17/DefaultOrEmpty</id>
   <content type="html">&lt;p&gt;Today I was reading the AutoFixture source code in GitHub and I found a LINQ method I didn’t know about: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DefaultIfEmpty&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DefaultIfEmpty returns a collection containing a single element if the source collection is empty. Otherwise, it returns the same source collection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, let’s find all the movies with a rating greater than 9. Otherwise, return our all-time favorite movie.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// We don&apos;t have movies with rating greater than 9&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Titanic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Back to the Future&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Black Hawk Down&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTimesFavorite&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fifth Element&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movieToWatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;movie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Score&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DefaultIfEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allTimesFavorite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Movie { Name=&quot;Fifth Element&quot;, Score=10 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If I had to implement it on my own, it would be like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultIfEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;@default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DefaultIfEmpty&lt;/code&gt; is helpful to make sure we always have a default value when filtering a collection. It’s a good alternative to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstOrDefault&lt;/code&gt; followed by a null guard.&lt;/p&gt;

&lt;p&gt;To learn more about LINQ, check my &lt;a href=&quot;/2021/01/18/LinqGuide/&quot;&gt;Quick Guide to LINQ with Examples&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;
Want to write more expressive code for collections? Join my course, &lt;a href=&quot;https://www.udemy.com/course/getting-started-with-linq-in-csharp/?referralCode=1B94DF2F5439E06B6397&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; data-goatcounter-click=&quot;LinqCourse-Footer&quot; data-goatcounter-title=&quot;Linq Course: Footer&quot;&gt;Getting Started with LINQ&lt;/a&gt; on Udemy and learn everything you need to know to start working productively with LINQ—in less than 2 hours.
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: SQL Server uses all available memory</title>
   <link href="https://canro91.github.io/2020/11/13/SQLServerMemory/"/>
   <updated>2020-11-13T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/11/13/SQLServerMemory</id>
   <content type="html">&lt;p&gt;SQL Server tries to use all available memory. SQL Server allocates memory during its activity. And, it only releases it when Windows asks for it.&lt;/p&gt;

&lt;p&gt;This is normal behavior. SQL Server caches data into memory to reduce access to disk. Remember, SQL Server caches data pages, not query results.&lt;/p&gt;

&lt;p&gt;You can limit the amount of memory available by setting the option “Maximum Server Memory”. By default, it is a ridiculous huge number: 2,147,483,647 MB.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-11-13-SQLServerMemory/SQLServerEatingMyRAM.png&quot; alt=&quot;SQL Server uses all available memory&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;SQL Server eating my RAM&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This is specially true, if you’re running SQL Server on your development machine.&lt;/p&gt;

&lt;p&gt;For your Production instances, check BornSQL’s &lt;a href=&quot;https://bornsql.ca/s/memory/&quot;&gt;Max Server Memory Matrix&lt;/a&gt; to set the right amount of RAM your SQL Server needs.&lt;/p&gt;

&lt;p&gt;Voilà! This is a true story of how SQL Server was eating my memory. We needed some limits to keep things running smoothly on my laptop.&lt;/p&gt;

&lt;p&gt;For more SQL Server content, check &lt;a href=&quot;/2020/09/28/SQLServerTuningTips/&quot;&gt;Six SQL Server performance tuning tips&lt;/a&gt; and &lt;a href=&quot;/2021/03/08/HowNotToWriteDynamicSQL/&quot;&gt;How to write dynamic SQL queries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source&lt;/em&gt;: &lt;a href=&quot;https://www.mssqltips.com/sqlservertip/4182/setting-a-fixed-amount-of-memory-for-sql-server/&quot;&gt;Setting a fixed amount of memory for SQL Server&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to write good unit tests: Noise and Hidden Test Values</title>
   <link href="https://canro91.github.io/2020/11/02/UnitTestingTips/"/>
   <updated>2020-11-02T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/11/02/UnitTestingTips</id>
   <content type="html">&lt;p&gt;These days, I needed to update some unit tests. I found two types of issues with them. Please, continue to read. Maybe, you’re a victim of those issues, too. Let’s learn how to write good unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To write good unit tests, avoid complex setup scenarios and hidden test values. Often tests are bloated with unneeded or complex code in the Arrange part and full of magic or hidden test values. Unit tests should be even more readable than production code&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-tests-i-had-to-update&quot;&gt;The tests I had to update&lt;/h2&gt;

&lt;p&gt;The tests I needed to update were for an ASP.NET Core API controller, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountController&lt;/code&gt;. This controller created, updated, and suspended user accounts. Also, it sent a welcome email to new users.&lt;/p&gt;

&lt;p&gt;These tests checked a configuration object for the sender, reply-to, and contact-us email addresses. The welcome email contained those three emails. If the configuration files miss one of the email addresses, the controller throws an exception from its constructor.&lt;/p&gt;

&lt;p&gt;Let’s see one of the tests. This test checks for the sender’s email.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController_SenderEmailIsNull_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountPersonService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountPersonService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpContextAccessor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IHttpContextAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetupGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ReplyToEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SupportEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;accountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;accountPersonService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;httpContextAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompletedTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This test uses &lt;a href=&quot;/2020/08/11/HowToCreateFakesWithMoq/&quot;&gt;Moq to create stubs and mocks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Can you spot any issues in our sample test? The naming convention isn’t one, by the way.&lt;/p&gt;

&lt;p&gt;Let’s see the two issues to avoid to write good unit tests.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/32/6Icr9fARMmTjTHqTzK8z_DSC_0123.jpg?ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;fm=jpg&amp;amp;crop=entropy&amp;amp;cs=tinysrgb&amp;amp;w=800&amp;amp;h=400&amp;amp;fit=crop&amp;amp;ixid=eyJhcHBfaWQiOjF9&quot; alt=&quot;Adjusting dials on a mixer&quot; /&gt;

&lt;figcaption&gt;&lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@drewpatrickmiller?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Drew Patrick Miller&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/73o_FzZ5x-w?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;1-reduce-the-noise&quot;&gt;1. Reduce the noise&lt;/h2&gt;

&lt;p&gt;Our sample test only cares about one object: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOptions&amp;lt;EmailConfiguration&amp;gt;&lt;/code&gt;. All other objects are noise for our test. They don’t have anything to do with the scenario under test. We have to use them to make our test compile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use builder methods to reduce complex setup scenarios.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s reduce the noise from our test with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MakeAccountController()&lt;/code&gt; method. It will receive the only parameter the test needs.&lt;/p&gt;

&lt;p&gt;After this change, our test looked like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController_SenderEmailIsNull_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//     ^^^^&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// We can make this test a void method&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetupGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ReplyToEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SupportEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Notice how we reduced the noise with a builder&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// We don&apos;t need a return statement here anymore&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountPersonService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IAccountPersonService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEmailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We don&apos;t need Mock&amp;lt;IOptions&amp;lt;EmailConfiguration&amp;gt;&amp;gt; here&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpContextAccessor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IHttpContextAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;accountService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;accountPersonService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;emailService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;emailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;httpContextAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, since our test doesn’t have any asynchronous code, we could declare our test as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void&lt;/code&gt; method and remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return&lt;/code&gt; statement. That looked weird in a unit test, in the first place.&lt;/p&gt;

&lt;p&gt;With this refactor, our test started to look simpler and easier to read. Now, it’s clear this test only cares about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmailConfiguration&lt;/code&gt; class.&lt;/p&gt;

&lt;h2 id=&quot;2-make-your-test-values-obvious&quot;&gt;2. Make your test values obvious&lt;/h2&gt;

&lt;p&gt;Our test states in its name that the sender’s email is null. Anyone reading this test would expect to see a variable set to null and passed around. But, that’s not the case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make scenarios under test and test values extremely obvious&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Please, don’t make developers decode your tests.&lt;/p&gt;

&lt;p&gt;To make the test scenario obvious in our example, let’s add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SenderEmail = null&lt;/code&gt; to the initialization of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmailConfiguration&lt;/code&gt; object.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController_SenderEmailIsNull_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetupGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// The test value is obvious now&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SenderEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//            ^^^^^&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ReplyToEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SupportEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we have similar scenarios, we can use a constant like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const string NoEmail = null&lt;/code&gt;. Or prefer &lt;a href=&quot;/2021/04/26/CreateTestValuesWithBuilders/&quot;&gt;object mothers and builders to create test data&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-dont-write-mocks-for-ioptions&quot;&gt;3. Don’t write mocks for IOptions&lt;T&gt;&lt;/T&gt;&lt;/h2&gt;

&lt;p&gt;Finally, as an aside, we don’t need a mock on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOptions&amp;lt;EmailConfiguration&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t use a mock or a stub with the IOptions interface. That would introduce extra complexity. Use Options.Create() with the value to configure instead.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option.Create()&lt;/code&gt; method instead.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AccountController_SenderEmailIsNull_ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EmailConfiguration&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                ^^^^^&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SenderEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ReplyToEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SupportEmail&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;email@email.com&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThrowsException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;MakeAccountController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emailConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s way easier to read. Do you have noise and hidden test values in your tests? Remember, readability is one of the pillars of unit testing. Don’t make developers decode your tests.&lt;/p&gt;

&lt;p&gt;For other tips on writing good unit tests, check my follow-ups on &lt;a href=&quot;/2021/02/05/FailingTest/&quot;&gt;writing failing tests first&lt;/a&gt; and &lt;a href=&quot;/2022/12/14/SimpleTestValues/&quot;&gt;using simple test values&lt;/a&gt;. Also, don’t miss my &lt;a href=&quot;/UnitTesting&quot;&gt;Unit Testing 101 series&lt;/a&gt; where I cover more subjects like this.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy unit testing!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Let&apos;s React. Learn React in 30 days</title>
   <link href="https://canro91.github.io/2020/10/26/ReactIn30Days/"/>
   <updated>2020-10-26T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/26/ReactIn30Days</id>
   <content type="html">&lt;p&gt;Do you want to learn React and you don’t where to start? Don’t look for any other curated list of resources. Let’s learn React in 30 days!&lt;/p&gt;

&lt;p&gt;React is a JavaScript library to build user interfaces. It doesn’t do a lot of things. It renders elements on the screen. Period! React isn’t a Swiss-army knife framework full of functionalities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To learn React in 30 days, start learning to create components and understand the difference between props and state. Next, learn about hooks and how to style components. After that, learn about managing state with hooks. But don’t rush to use Redux from the beginning.&lt;/strong&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1485783522162-1dbb8ffcbe5b?ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;fm=jpg&amp;amp;crop=entropy&amp;amp;cs=tinysrgb&amp;amp;w=800&amp;amp;h=400&amp;amp;fit=crop&amp;amp;ixid=eyJhcHBfaWQiOjF9&quot; alt=&quot;Building a rocket ship&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Building a rocket ship. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@kellysikkema?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Kelly Sikkema&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/blocks?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Instead of reading books from cover to cover or passively watching YouTube videos, let’s learn React by doing, by getting our hands dirty. Let’s recreate examples, build mini-projects, and stop copy-pasting. If you’re instered in learning strategies, check my takeaways from the &lt;a href=&quot;/2020/07/14/UltralearningTakeaways/&quot;&gt;Ultralearning book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These are some resources to learn React, its prerequisites, and related subjects.&lt;/p&gt;

&lt;h2 id=&quot;1-prerequisites&quot;&gt;1. Prerequisites&lt;/h2&gt;

&lt;p&gt;Before starting to work with React, make sure to know about flexbox in CSS and ES6 JavaScript features.&lt;/p&gt;

&lt;h3 id=&quot;css&quot;&gt;CSS&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/implement-a-design-with-css/&quot;&gt;CSS Layout tutorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=5bMdjkfvONE&quot;&gt;Build a Professional Website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;javascript&quot;&gt;JavaScript&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript&quot;&gt;A re-introduction to Javascript&lt;/a&gt; or &lt;a href=&quot;https://www.youtube.com/watch?v=W6NZfCO5SIk&quot;&gt;Learn Javascript in an hour&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=NCwa_xi0Uuc&quot;&gt;ES6 Tutorial&lt;/a&gt; or &lt;a href=&quot;https://www.coursera.org/projects/modern-javascript-es6-basics?edocomorp=freegpmay2020&quot;&gt;Coursera ES6 Basics&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Follow some Vanilla projects:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=2VJlzeEVL8A&quot;&gt;Music Box&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ttf3CEsEwMQ&quot;&gt;Todo App&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=HEsAr2Yt2do&quot;&gt;Tetris Game&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=JnEH9tYLxLk&quot;&gt;Twitter clone&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aql62xliRzE&quot;&gt;Product catalog&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=023Psne_-_4&quot;&gt;E-commerce site&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Q4RiI7bvpso&quot;&gt;YouTube API Proxy server&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these projects include the backend side using Node.js. You can find more vanilla projects without any backend code on &lt;a href=&quot;https://dev.to/nerdjfpb/15-vanilla-javascript-project-ideas-beginner-to-expert-with-free-tutorial-3c7a&quot;&gt;15 Vanilla Project Ideas&lt;/a&gt; and &lt;a href=&quot;https://github.com/bradtraversy/vanillawebprojects&quot;&gt;20+ Web Projects With Vanilla JavaScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don’t mess your environment with different versions of Node. Follow &lt;a href=&quot;https://youknowfordevs.com/2018/07/01/dont-install-node-until-youve-read-this.html&quot;&gt;Don’t Install Node Until You’ve Read This&lt;/a&gt; and &lt;a href=&quot;https://youknowfordevs.com/2018/07/02/your-development-workflow-just-got-better-with-docker-compose.html&quot;&gt;Your Development Workflow Just Got Better, With Docker Compose&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;2-react&quot;&gt;2. React&lt;/h2&gt;

&lt;h3 id=&quot;study-plans&quot;&gt;Study Plans&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/a-study-plan-to-cure-javascript-fatigue-8ad3a54f2eb1/&quot;&gt;4-Week Plan: A Study Plan To Cure JavaScript Fatigue&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/learning-react-roadmap-from-scratch-to-advanced-bff7735531b6/&quot;&gt;How to Learn React — A roadmap from beginner to advanced&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.isquaredsoftware.com/2017/12/blogged-answers-learn-react/&quot;&gt;Resources for Learning React&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.robinwieruch.de/learn-react-js&quot;&gt;How to learn React.js in 2020&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/thetiltblog/simple-guide-to-learning-react-from-scratch-c11fcccd3e76&quot;&gt;A simple guide to learning React from scratch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;react-first-steps&quot;&gt;React: First steps&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/what-react-does/&quot;&gt;What React Does (and Doesn’t Do)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.taniarascia.com/getting-started-with-react/&quot;&gt;React Tutorial: An Overview and Walkthrough&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/react-getting-started-tutorial/&quot;&gt;React: Getting Started - The Complete Tutorial for 2020&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;From React official docs:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://reactjs.org/tutorial/tutorial.html&quot;&gt;Tutorial: Intro to React&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;If you prefer learning concepts first, &lt;a href=&quot;https://reactjs.org/docs/hello-world.html&quot;&gt;Hello World&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/thinking-in-react.html&quot;&gt;Thinking in React&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/composition-vs-inheritance.html&quot;&gt;Composition vs inheritance&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;react-hooks&quot;&gt;React Hooks&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/intro-to-hooks/&quot;&gt;A Simple Intro to React Hooks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.robinwieruch.de/react-hooks-fetch-data&quot;&gt;How to fetch data with React Hooks?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/useeffect-triggers-every-change&quot;&gt;Fix useEffect re-running on every render&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://overreacted.io/a-complete-guide-to-useeffect/&quot;&gt;A Complete Guide to useEffect&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/usereducer-hook-examples/&quot;&gt;Examples of the useReducer Hook&lt;/a&gt; and &lt;a href=&quot;https://www.robinwieruch.de/react-usereducer-hook&quot;&gt;How to useReducer in React&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.robinwieruch.de/react-context&quot;&gt;React Context&lt;/a&gt; and &lt;a href=&quot;https://www.robinwieruch.de/react-usecontext-hook&quot;&gt;How to useContext in React?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.robinwieruch.de/react-state-usereducer-usestate-usecontext&quot;&gt;React State Hooks: useReducer, useState, useContext&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.lorenzweiss.de/common_mistakes_react_hooks/&quot;&gt;Five common mistakes writing react components (with hooks) in 2020&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;walk-throughs&quot;&gt;Walk-throughs&lt;/h3&gt;

&lt;h4 id=&quot;basic--intermediate&quot;&gt;Basic &amp;amp; Intermediate&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/create-gameoflife-with-react-in-one-hour-8e686a410174/&quot;&gt;Game of live&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/how-to-build-a-markdown-previewer-with-react-js/&quot;&gt;Markdown preview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=U9T6YkEDkMo&quot;&gt;Recipe App&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ev9YLaLvAJM&quot;&gt;Bookmark app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aq-fCtg_gG4&quot;&gt;Quiz&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=KzYUuTiHdiY&quot;&gt;Calculator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tOK9l5uP06U&quot;&gt;Build a website&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=IxuqmfO6p28&quot;&gt;Weather app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=sZ0bZGfg_m4&quot;&gt;Movie list app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=zT62eVxShsY&quot;&gt;Multi Step Form With React &amp;amp; Material UI&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=VPVzx1ZOVuw&quot;&gt;YouTube clone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;advanced&quot;&gt;Advanced&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XuFDcZABiDQ&quot;&gt;Expense tracker&lt;/a&gt; and its &lt;a href=&quot;https://www.youtube.com/watch?v=KyWaXA_NvT0&quot;&gt;backend&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jFNHerJqvFw&quot;&gt;Chat with Chatkit&lt;/a&gt; or with &lt;a href=&quot;https://www.youtube.com/watch?v=ZwFA3YMfkoc&quot;&gt;Socket.io&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial&quot;&gt;Firebase Authentication in React Tutorial for Beginners&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.newline.co/fullstack-react/articles/react-tutorial-cloning-yelp/&quot;&gt;Yelp clone&lt;/a&gt;. To start with TDD in React, &lt;a href=&quot;https://daveceddia.com/getting-started-with-tdd-in-react/&quot;&gt;Getting Started with TDD in React&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=5pQsl9u_10M&quot;&gt;Travel Log&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=HgfA4W_VjmI&quot;&gt;Todoist clone&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=7DLRJj1YjvQ&quot;&gt;Building a Newsreader&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Fy9SdZLBTOo&quot;&gt;eCommerce site&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LXJOvkVYQqA&quot;&gt;Reservation app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=x_EEwGe-a9o&quot;&gt;Netflix clone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-react-and-redux&quot;&gt;3. React and Redux&lt;/h2&gt;

&lt;p&gt;Redux could be the most challenging subject. You have to learn new concepts like: store, actions, reducers, thunks, sagas, dispatching.&lt;/p&gt;

&lt;p&gt;Before getting into Redux, start by learning how to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useState&lt;/code&gt; hook, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useReducer&lt;/code&gt;, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useContext&lt;/code&gt;, and last Redux. It feels more natural this way.&lt;/p&gt;

&lt;p&gt;Make sure to understand what to put into a Redux store and where you should make your API calls. Be aware &lt;a href=&quot;https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367&quot;&gt;you might not need Redux&lt;/a&gt; at all.&lt;/p&gt;

&lt;h3 id=&quot;tutorials&quot;&gt;Tutorials&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.robinwieruch.de/learn-react-before-using-redux&quot;&gt;8 things to learn in React before using Redux&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/what-does-redux-do/&quot;&gt;What Does Redux Do?&lt;/a&gt;, &lt;a href=&quot;https://daveceddia.com/redux-tutorial/&quot;&gt;A Complete React Redux Tutorial for Beginners&lt;/a&gt; and &lt;a href=&quot;https://daveceddia.com/react-redux-immutability-guide/&quot;&gt;Immutability in React and Redux: The Complete Guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/context-api-vs-redux/&quot;&gt;Redux vs. The React Context API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.robinwieruch.de/react-redux-tutorial&quot;&gt;React Redux Tutorial for Beginners&lt;/a&gt; This is a complete Redux tutorial. It covers almost everything you need to know, from creating an store to testing your reducers.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://css-tricks.com/learning-react-redux/&quot;&gt;Leveling Up with React: Redux&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=poQXNp9ItL4&quot;&gt;Redux Tutorial - Learn Redux from Scratch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;walk-throughs-1&quot;&gt;Walk-throughs&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=WpvIihorarA&quot;&gt;Simple Notes app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=KLCnTjB0w_o&quot;&gt;Shopping Cart Checkout Summary&lt;/a&gt; An interview exercise. Try to add Redux on your own&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ia0WWc5dyLE&amp;amp;list=PLG3RxIUKLJlbDDGeeoUCkinS2DUybp_1o&quot;&gt;Movie app with Redux&lt;/a&gt; Again try to add Redux on your own.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://auth0.com/blog/developing-games-with-react-redux-and-svg-part-1/&quot;&gt;Developing Games with React, Redux, and SVG&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4-courses&quot;&gt;4. Courses&lt;/h2&gt;

&lt;h3 id=&quot;paid&quot;&gt;Paid&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.pluralsight.com/courses/react-js-getting-started&quot;&gt;Pluralsight React.js Getting Started&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.coursera.org/specializations/full-stack-react&quot;&gt;Coursera Full Stack React&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.udemy.com/course/mern-stack-master-course-building-your-own-instagram/?couponCode=FREEFREEFREE&quot;&gt;Udemy Instagram clone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;free&quot;&gt;Free&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/the-beginner-s-guide-to-react&quot;&gt;The Beginner’s Guide to React&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ui.dev/free-react-bootcamp/&quot;&gt;The Free React Bootcamp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://scrimba.com/g/glearnreact&quot;&gt;Learn React for free&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.newline.co/fullstack-react/30-days-of-react/&quot;&gt;30 days of React&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4lF7IiIHliU&quot;&gt;Learn Building Applications with React and Redux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-practice-and-project-ideas&quot;&gt;5. Practice and Project Ideas&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/learn-react-with-copywork/&quot;&gt;Cheat to Win: Learn React with Copywork&lt;/a&gt;. Two projects to copy: &lt;a href=&quot;https://github.com/taming-the-state-in-react/react-snake&quot;&gt;React Snake&lt;/a&gt;, &lt;a href=&quot;https://github.com/dceddia/github-issues-viewer&quot;&gt;GitHub issue viewer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hackernoon.com/hands-on-projects-to-learn-the-basics-of-react-3a06726514a8&quot;&gt;Hands-on projects to learn the basics of React&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/react-practice-projects/&quot;&gt;6 Fun React Project Ideas&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://codeartistry.io/5-react-projects-you-need/&quot;&gt;5 React Projects You Need In Your Portfolio&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@dtkatz/10-react-starter-project-ideas-to-get-you-coding-5b35782e1831&quot;&gt;10 React Starter Project Ideas to Get You Coding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;6-other-resources&quot;&gt;6. Other resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://daveceddia.com/react-project-structure/&quot;&gt;How to Structure Your React Project&lt;/a&gt; or &lt;a href=&quot;https://marmelab.com/blog/2015/12/17/react-directory-structure.html&quot;&gt;React directory structure&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/building-chrome-extensions-in-react-parcel-79d0240dd58f/&quot;&gt;How to build Chrome extensions with React + Parcel&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://youtu.be/f2mMOiCSj5c&quot;&gt;React internals&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://zapier.com/engineering/how-to-build-redux/&quot;&gt;Build Yourself a Redux&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=1GNPzfcElyo&quot;&gt;Frontend Framework Showdown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voilà! Those are the resources and project ideas to learn React in 30 days. Start small creating components and understanding the difference between props and states. You can find my own 30-day journey following the resources from this post in my GitHub &lt;a href=&quot;https://github.com/canro91/LetsReact&quot;&gt;LetsReact&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/canro91/LetsReact&quot;&gt;&lt;img src=&quot;https://gh-card.dev/repos/canro91/LetsReact.svg&quot; alt=&quot;canro91/LetsReact - GitHub&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in learning projects, check &lt;a href=&quot;/2020/07/05/LetsGoStudyPlan/&quot;&gt;Let’s Go&lt;/a&gt; and my &lt;a href=&quot;/2023/07/24/AdviceToStartAnUltralearningProject/&quot;&gt;advice to start an ultralearning project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Three Language Lessons I Learned on my First Visit to France</title>
   <link href="https://canro91.github.io/2020/10/23/ThreeLanguageLessons/"/>
   <updated>2020-10-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/23/ThreeLanguageLessons</id>
   <content type="html">&lt;p&gt;This post is about one of my hobbies: learning new languages. But not programming languages. Foreign languages.&lt;/p&gt;

&lt;p&gt;I want to share three lessons I learned while traveling to France to practice my French-speaking skills. Each lesson is behind a funny story that happened on my first visit to France.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1616937927892-074b4217febc?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&amp;amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA&quot; width=&quot;600&quot; alt=&quot;Three language lessons I learned in my First visit to France&quot; /&gt;

&lt;figcaption&gt;Photo by &lt;a href=&quot;https://unsplash.com/@bottlecow?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Byeong woo Kang&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/eiffel-tower-under-white-clouds-during-daytime-CF6Ev7NCw_Y?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&quot;&gt;Unsplash&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;These are the three lessons I learned:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Don’t Fall into the temptation of speaking English.&lt;/strong&gt; Keep speaking in your target language when locals talk to you in English. Don’t think they’re rude or you aren’t “worthy” of their language. They want to practice too. Ask politely to continue speaking in your target language. Or pretend you don’t speak English.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Learn vocabulary to suit your needs.&lt;/strong&gt; If you’re traveling for work, vacations, or cultural exchange, you will need vocabulary for totally different situations. I learned this lesson while waiting at the security at an airport. Can you imagine what happened?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mistakes are progress.&lt;/strong&gt; Embrace it when locals correct your language skills. Don’t feel discouraged when locals correct your mistakes. Imagine you got a free language lesson. Probably you won’t make that mistake again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To read the full story, go to “Fluent in 3 Months” at &lt;a href=&quot;https://www.fluentin3months.com/speaking-french-in-france/&quot;&gt;3 Language Lessons I Learned on my First Visit to France&lt;/a&gt; where I originally published it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy learning!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: NULL isn&apos;t LIKE anything else in SQL Server</title>
   <link href="https://canro91.github.io/2020/10/20/LikeWithNullSQLServer/"/>
   <updated>2020-10-20T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/20/LikeWithNullSQLServer</id>
   <content type="html">&lt;p&gt;How does the LIKE operator handle NULL values of a column? Let’s see what SQL Server does when using LIKE with a nullable column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When using the LIKE operator on a nullable column, SQL Server doesn’t include in the results rows with NULL values in that column. The same is true, when using NOT LIKE in a WHERE clause.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s see an example. Let’s create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Client&lt;/code&gt; table with an ID, name and middleName. Only two of the four sample clients have a middlename.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Clients&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MiddleName&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Clients&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Alice&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;A&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Bob&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Charlie&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;C&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Dwight&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s find all users with middlename starting and not starting with ‘A’.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Clients&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MiddleName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;A%&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Clients&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MiddleName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;A%&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice the results don’t include any rows with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NULL&lt;/code&gt; middlenames.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-10-20-LikeWithNullSQLServer/Null&amp;amp;Like.png&quot; alt=&quot;Results of querying a nullable column with LIKE&quot; /&gt;
    &lt;figcaption&gt;Results of querying a nullable column with LIKE and NOT LIKE&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Voilà! That’s how SQL Server handle NULL when using LIKE and NOT LIKE. Remember you don’t need to check for null values.&lt;/p&gt;

&lt;p&gt;If you want to read more SQL Server content, check &lt;a href=&quot;/2020/09/28/SQLServerTuningTips/&quot;&gt;six performance tuning tips&lt;/a&gt; and the lessons learned while &lt;a href=&quot;/2020/10/14/SearchingReservations/&quot;&gt;tuning a store procedure to search reservations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source&lt;/em&gt;: &lt;a href=&quot;https://weblogs.sqlteam.com/markc/2009/06/08/60929/&quot;&gt;NULL is NOT LIKE and NOT NOT LIKE&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>BugOfTheDay: How I tuned a procedure to find reservations</title>
   <link href="https://canro91.github.io/2020/10/14/SearchingReservations/"/>
   <updated>2020-10-14T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/14/SearchingReservations</id>
   <content type="html">&lt;p&gt;This time, one of the searching features for reservations was timing out. The appropiate store procedure took ~5 minutes to finish. This is how I tuned it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To tune a store procedure, start by looking for expensive operators in its Actual Execution plan. Reduce the number of joining tables and stay away from common bad practices like putting functions around columns in WHERE clauses.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After opening the actual exection plan with &lt;a href=&quot;https://www.sentryone.com/plan-explorer&quot;&gt;SentryOne Plan Explorer&lt;/a&gt;, the most-CPU expensive and slowest statement looked like this:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resTemp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reservationID&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reservationID&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resTemp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reservations&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reservationID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reservationID&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accounts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt;
                  &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnWithAccountID&lt;/span&gt;
                  &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnWithAccountIDToo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientID&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumberAlpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This query belonged to a store procedure to search reservations by a bunch of filters. Among its filters, a hotelier can find all reservations assigned to a client’s internal account number.&lt;/p&gt;

&lt;p&gt;From the above query, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#resTemp&lt;/code&gt; table had reservations from previous queries in the same store procedure. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt; statement removes all reservations without the given account number.&lt;/p&gt;

&lt;p&gt;Inside SQL Server Management Studio, the store procedure did about 193 millions of logical reads to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.accounts&lt;/code&gt; table. That’s a lot!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For SQL Server, logical reads are the number of 8KB pages that SQL Server has to read to execute a query. Generally, the fewer logical reads, the faster a query runs.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;1-remove-extra-joins&quot;&gt;1. Remove extra joins&lt;/h2&gt;

&lt;p&gt;The subquery in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;  joined the found reservations with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.reservations&lt;/code&gt; table. And then, it joined the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.accounts&lt;/code&gt; table checking for any of the three columns with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accountID&lt;/code&gt;. &lt;em&gt;Yes, a reservation could have an accountID in three columns in the same table. Don’t ask me why.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This subquery performed an Index Scan on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.reservations&lt;/code&gt; table. It had a couple of millions of records. That’s the main table in any Reservation Management System.&lt;/p&gt;

&lt;p&gt;To remove the extra join to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.reservations&lt;/code&gt; table in the subquery, I added the three referenced columns (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accountID&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;columnWithAccountID&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;columnWithAccountIDToo&lt;/code&gt;) inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ON&lt;/code&gt; joining the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.accounts&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#resTemp&lt;/code&gt; temporary table. &lt;em&gt;By the way, those aren’t the real names of those columns.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After this change,  the store procedure took ~8 seconds. It read about 165,000  pages for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.accounts&lt;/code&gt; table. Wow!&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resTemp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reservationID&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reservationID&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resTemp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* We don&apos;t need the extra JOIN here */&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accounts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt;
                  &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnWithAccountID&lt;/span&gt;
                  &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnWithAccountIDToo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientID&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumberAlpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-use-not-exists&quot;&gt;2. Use NOT EXISTS&lt;/h2&gt;

&lt;p&gt;Then, instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NOT IN&lt;/code&gt;, I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NOT EXISTS&lt;/code&gt;. This way, I could lead the subquery from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.accounts&lt;/code&gt; table. Another JOIN gone!&lt;/p&gt;

&lt;p&gt;After this change, the store procedure finished in about 5 seconds.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resTemp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* Again, we got rid of another JOIN */&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accounts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnWithAccountID&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnWithAccountIDToo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientID&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumberAlpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Those ~4-5 seconds were good enough. But, there was still room for improvement.&lt;/p&gt;

&lt;div class=&quot;message&quot;&gt;If you&apos;re wondering about that weird SELECT 1/0, check my post on &lt;a href=&quot;/2020/10/08/ExistsSelectSQLServer/&quot;&gt;EXISTS SELECT in SQL Server&lt;/a&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-dont-use-functions-in-wheres&quot;&gt;3. Don’t use functions in WHERE’s&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISNULL()&lt;/code&gt; functions in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; look weird. &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;Using functions around columns in WHERE clauses&lt;/a&gt; is a common anti-pattern.&lt;/p&gt;

&lt;p&gt;In this case, a computed column concatenating the two parts of account numbers would help. &lt;em&gt;Yes, account numbers were stored splitted into two columns. Again, don’t ask me why.&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accounts&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountNumberComplete&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountNumberAlpha&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I didn’t use a persisted column. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.accounts&lt;/code&gt; table was a huge table, creating a persisted columns would have required scanning the whole table. I only wanted SQL Server to have better statistics to run the DELETE statement.&lt;/p&gt;

&lt;p&gt;To take things even further, an index leading on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClientId&lt;/code&gt; followed by that computed column could make things even faster.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClientID_AccountNumberComplete&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accounts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountNumberComplete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I didn’t need to include the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;accountId&lt;/code&gt; on the index definition since it was the primary key  of the table.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how I tuned this query. The lesson to take home is to reduce the number of joining tables and stay away from functions in your WHERE’s. Often, a computed column can help SQL Server to run queries  with functions in the WHERE clause. Even, without rewriting the query to use the new computed column.&lt;/p&gt;

&lt;p&gt;For more content about SQL Server, check &lt;a href=&quot;/2020/09/28/SQLServerTuningTips/&quot;&gt;Six SQL Server tuning tips&lt;/a&gt; and &lt;a href=&quot;/2020/09/30/FormatSQL/&quot;&gt;Two free tools to format your SQL queries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: EXISTS SELECT 1 vs EXISTS SELECT * in SQL Server</title>
   <link href="https://canro91.github.io/2020/10/08/ExistsSelectSQLServer/"/>
   <updated>2020-10-08T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/08/ExistsSelectSQLServer</id>
   <content type="html">&lt;p&gt;EXISTS is a logical operator that checks if a subquery returns any rows. EXISTS works only with SELECT statements inside the subquery. Let’s see if there are any differences between EXISTS with SELECT * and SELECT 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no difference between EXISTS with SELECT * and SELECT 1. SQL Server generates similar execution plans in both scenarios. EXISTS returns true if the subquery returns one or more records, even if it returns NULL or 1/0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s use a local copy of the &lt;a href=&quot;https://www.brentozar.com/archive/2015/10/how-to-download-the-stack-overflow-database-via-bittorrent/&quot;&gt;StackOverflow database&lt;/a&gt; to find users from Antartica who have left any comments. Yes, the same StackOverflow we use everyday to copy and paste code.&lt;/p&gt;

&lt;p&gt;Let’s check how the execution plans look like when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT 1&lt;/code&gt; in the subquery with the EXISTS operator.&lt;/p&gt;

&lt;h2 id=&quot;1-exists-with-select-&quot;&gt;1. EXISTS with “SELECT *”&lt;/h2&gt;

&lt;p&gt;This is the query to find all users from Antartica who have commented anything. This query uses EXISTS with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Antartica&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXISTS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Comments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UserId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--         ^^^^^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make things faster, let’s add one index on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; and another one on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserId&lt;/code&gt; on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Users&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Comments&lt;/code&gt; tables, respectively.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserId&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UserId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s check the execution plan. Notice the “Left Semi Join” operator and the other operators.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-10-08-ExistsSelectSQLServer/Exists_Start.png&quot; alt=&quot;Execution plan using EXISTS with &apos;SELECT *&apos;&quot; /&gt;
    &lt;figcaption&gt;Execution plan using EXISTS with &apos;SELECT *&apos;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-exists-with-select-1&quot;&gt;2. EXISTS with “SELECT 1”&lt;/h2&gt;

&lt;p&gt;Now, let’s change the subquery inside the EXISTS to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT 1&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Antartica&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXISTS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Comments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UserId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--         ^^^^^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, let’s see the execution plan.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-10-08-ExistsSelectSQLServer/Exists1.png&quot; alt=&quot;Execution plan using EXISTS with &apos;SELECT 1&apos;&quot; /&gt;
    &lt;figcaption&gt;Execution plan using EXISTS with &apos;SELECT 1&apos;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Voilà! Notice, there is no difference between the two execution plans when using EXISTS with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT 1&lt;/code&gt;. We don’t need to write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT TOP 1 1&lt;/code&gt; inside our EXISTS subqueries. We can even rewrite our queries to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT NULL&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT 1/0&lt;/code&gt; without any division-by-zero error.&lt;/p&gt;

&lt;p&gt;If you want to read more SQL and SQL Server content, check &lt;a href=&quot;/2021/03/08/HowNotToWriteDynamicSQL/&quot;&gt;how to write Dynamic SQL&lt;/a&gt; and &lt;a href=&quot;/2021/01/04/TruncateVsDelete/&quot;&gt;three differences between TRUNCATE and DELETE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy SQL time!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to compare DateTime without the time part in SQL Server</title>
   <link href="https://canro91.github.io/2020/10/05/CompareDateTimeSQLServer/"/>
   <updated>2020-10-05T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/05/CompareDateTimeSQLServer</id>
   <content type="html">&lt;p&gt;If you use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DATEDIFF()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAST()&lt;/code&gt; to filter a table by a DATETIME column using only the date part, there’s a better way. Let’s find it out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To compare dates without the time part, don’t use the DATEDIFF() or any other function on both sides of the comparison in a WHERE clause. Instead, put CAST() on the parameter and compare using &amp;gt;= and &amp;lt; operators&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s use a local copy of the &lt;a href=&quot;https://www.brentozar.com/archive/2015/10/how-to-download-the-stack-overflow-database-via-bittorrent/&quot;&gt;StackOverflow database&lt;/a&gt; to find all user profiles created on a particular date.&lt;/p&gt;

&lt;p&gt;Inside StackOverflow database, there’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Users&lt;/code&gt; table with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreationDate&lt;/code&gt; column. Let’s use that column to find all users who created their profiles today.&lt;/p&gt;

&lt;p&gt;Before we get started, let’s create an index on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreationDate&lt;/code&gt; to make things faster.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Probably, we would write a query like this one,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DATEDIFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, SQL Server has to scan the entire index. Notice the “Index Scan” operator in the execution plan. SQL Server doesn’t have any statistics on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreationDate&lt;/code&gt; wrapped in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DATEDIFF()&lt;/code&gt; function.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-10-05-CompareDateTimeSQLServer/DATEDIFF.png&quot; alt=&quot;Execution plan filtering a DateTime column with DATEDIFF&quot; /&gt;
    &lt;figcaption&gt;Execution plan filtering a DateTime column with DATEDIFF&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;An Index Scan by itself in an execution plan isn’t good or bad. It depends on the number of rows read&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this case, SQL Server read all the records on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Users&lt;/code&gt; table. When we hover over the row next to the “Index Scan” operator, we notice the number of rows read. It scanned the whole index, more than 2 millions of records.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-10-05-CompareDateTimeSQLServer/Rows.png&quot; alt=&quot;Execution plan showing the rows read&quot; /&gt;
    &lt;figcaption&gt;Execution plan showing the rows read&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Let’s stop using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DATEDIFF()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAST()&lt;/code&gt; to filter a table on a DATETIME column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To filter a table on a DATETIME column comparing only the date part, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAST()&lt;/code&gt; only around the parameter, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;=&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; with the desired date and the day after.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Users&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreationDate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DATEADD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s how to compare dates on the WHERE clauses. Don’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DATEDIFF()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAST()&lt;/code&gt; on both sides of the comparison. In general, &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;don’t put functions around columns&lt;/a&gt; in the WHERE clause.&lt;/p&gt;

&lt;p&gt;For more content about SQL Server, check the &lt;a href=&quot;/2020/10/08/ExistsSelectSQLServer/&quot;&gt;difference between EXISTS SELECT 1 and EXISTS SELECT *&lt;/a&gt;, &lt;a href=&quot;/2022/04/04/TSQLDoesNotHaveConstants/&quot;&gt;T-SQL doesn’t have constants and variables aren’t a good idea&lt;/a&gt;, and &lt;a href=&quot;/2020/10/20/LikeWithNullSQLServer/&quot;&gt;how LIKE handle NULL values&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source&lt;/em&gt;: &lt;a href=&quot;https://dba.stackexchange.com/questions/128235/optimized-date-compare-in-where-clause-convert-or-datediff-0&quot;&gt;Optimized date compare in WHERE clause&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Show your work. Takeaways</title>
   <link href="https://canro91.github.io/2020/10/01/ShowYourWorkTakeaways/"/>
   <updated>2020-10-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/01/ShowYourWorkTakeaways</id>
   <content type="html">&lt;p&gt;Show your work is a New York Times bestseller by Austin Kleon. He describes his book as &lt;em&gt;“a book for people who hate the very idea of self-promotion”&lt;/em&gt;. This book tells you how and why you should show your work online. These are my takeaways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Show your work” teaches that your work has to be out there. And, if your work isn’t online, it doesn’t exist. Good work isn’t enough.&lt;/strong&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1506845347893-bc5faede1eec?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0MDc0OTI3MA&amp;amp;ixlib=rb-1.2.1&amp;amp;q=80&amp;amp;utm_campaign=api-credit&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash_source&amp;amp;w=600&quot; alt=&quot;Kid walking in a museum&quot; /&gt;

&lt;figcaption&gt;Natural History Museum, London, United Kingdom. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@mparzuchowski?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Michał Parzuchowski&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/museum?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;“Be so good they can’t ignore you”&lt;/strong&gt; summarizes the purpose of the book. To not be ignored, you have to be findable. Build a routine of sharing. Take advantage of your network. Imagine you don’t need a resume because your next boss already reads your blog.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Be so good they can’t ignore you.”&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;-Steve Martin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;you-dont-have-to-be-a-genius&quot;&gt;You don’t have to be a genius&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Creativity is about collaboration, too&lt;/strong&gt;. Find a place where you can share your ideas and flourish your creativity. Find your creativity circle or “scenius”. Famous musicians and artists were surrounded by other artists to share, copy, acknowledge and sparkle their ideas.&lt;/p&gt;

&lt;p&gt;Ask yourself what you want to learn. And make a commitment to learning in front of others. Share what you love, and the people who love the same thing will find you.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-10-01-ShowYourWorkTakeaways/Quote.png&quot; alt=&quot;The minute you learn something, teach it&quot; /&gt;
    &lt;figcaption&gt;My favorite quote from Show Your Work&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;share-something-small-every-day&quot;&gt;Share something small every day&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There is no such a thing as overnight success&lt;/strong&gt;. At the end of the day, see what you can share: inspiration, progress, or learning. Share what you think it’s helpful or entertaining.&lt;/p&gt;

&lt;p&gt;Don’t be afraid of sharing your work. 90% of everything is crap. But don’t turn into human spam.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Stop worrying, start sharing”&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;-From Show your work &lt;a href=&quot;https://austinkleon.com/show-your-work/&quot;&gt;official trailer&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;open-up-your-cabinet-of-curiosities&quot;&gt;Open up your cabinet of curiosities&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Have a cabinet of curiosities&lt;/strong&gt;. Before museums, people had a place to put what they loved, usually rare and weird things. Think of yourself as a collector.&lt;/p&gt;

&lt;p&gt;Before you can share your work, you can share your taste. You can share what you read, who you follow, what inspires you. Credit your sources. Don’t share things you can’t credit.&lt;/p&gt;

&lt;p&gt;Voilà! These are my takeaways from Show Your Work. This isn’t a programming book, but it has inspired me to continue writing, even when I think nobody is reading.&lt;/p&gt;

&lt;p&gt;If you want to read other takeaways, check &lt;a href=&quot;/2020/06/15/CleanCoder/&quot;&gt;Clean Coder&lt;/a&gt; and &lt;a href=&quot;/2020/05/07/PragmaticThinkingAndLearning/&quot;&gt;Pragmatic Thinking and Learning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#showyourwork&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>TIL: How to add gzip compression to ASP.NET Core API responses</title>
   <link href="https://canro91.github.io/2020/10/01/CompressResponses/"/>
   <updated>2020-10-01T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/10/01/CompressResponses</id>
   <content type="html">&lt;p&gt;Today, I got the report that one API endpoint took minutes to respond. It turned out that it returned hundreds of large complex objects. Those objects contained branding colors, copy text, and hotel configurations in a reservation system. This is how to add response compression in ASP.NET Core 6.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To compress responses with ASP.NET Core, register the default compression providers into the dependencies container with the UseResponseCompression() method.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Something like this,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddResponseCompression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//               ^^^^^&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseResponseCompression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//  ^^^^^&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we don’t specify any compression provider, ASP.NET Core uses a default one.&lt;/p&gt;

&lt;p&gt;If we want gzip compression, then let’s register the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GzipCompressionProvider&lt;/code&gt; inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddResponseCompression()&lt;/code&gt; and set its compression level by configuring the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GzipCompressionProviderOptions&lt;/code&gt;,&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WebApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddResponseCompression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GzipCompressionProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GzipCompressionProviderOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Level&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CompressionLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fastest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//      ^^^^^&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseResponseCompression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For my slow endpoint, the easiest solution to speed it up was mapping my huge complex object to a new view model that only contained the properties the client side needed. I rolled a simple extension method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapToMyNewSimplifiedViewModel()&lt;/code&gt; for that.&lt;/p&gt;

&lt;p&gt;Voilà! That’s how to add gzip compression to responses with ASP.NET Core 6.0. That’s what I learned today.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;UPDATE (Oct 2023)&lt;/strong&gt;: In previous versions of ASP.NET Core, we needed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.AspNetCore.ResponseCompression&lt;/code&gt; NuGet package. It’s deprecated. ASP.NET Core has response compression built in now. We don’t need NuGet packages for this.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For more ASP.NET Core content, check &lt;a href=&quot;/2020/08/21/HowToConfigureValues/&quot;&gt;how to read configuration values&lt;/a&gt;, &lt;a href=&quot;/2020/06/29/HowToAddACacheLayer/&quot;&gt;how to create a caching layer&lt;/a&gt;, and &lt;a href=&quot;/2022/12/06/BackgroundServicesAndLiteHangfire/&quot;&gt;how to use background services with Hangfire&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source&lt;/em&gt;: &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/performance/response-compression?view=aspnetcore-6.0#gzip-compression-provider&quot;&gt;Response compression in ASP.NET Core&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Two free tools to format SQL queries</title>
   <link href="https://canro91.github.io/2020/09/30/FormatSQL/"/>
   <updated>2020-09-30T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/09/30/FormatSQL</id>
   <content type="html">&lt;p&gt;Do you need to format your SQL queries? Are you doing it by hand? Stop! There is a better way!&lt;/p&gt;

&lt;p&gt;Instead of formatting SQL queries to follow code conventions by hand, we can use online tools or extensions inside Visual Studio, SQL Server Management Studio, or any other text editor.&lt;/p&gt;

&lt;p&gt;These are two free tools to format SQL queries and store procedures. Inside Notepad++, use &lt;strong&gt;Poor Man’s T-SQL Formatter&lt;/strong&gt;. And, &lt;strong&gt;ApexSQL Refactor&lt;/strong&gt; for Visual Studio and SQL Server Management Studio.&lt;/p&gt;

&lt;h2 id=&quot;before&quot;&gt;Before&lt;/h2&gt;

&lt;p&gt;Before using Poor Man’s T-SQL Formatter and ApexSQL Refactor, I spent too much time formatting queries by hand. I mean making keywords uppercase, aligning columns, and arranging spaces.&lt;/p&gt;

&lt;p&gt;I tried to use the “Find and Replace” option inside a text editor. But it only worked for making keywords uppercase. Sometimes, I ended up messing with variables, parameters, and other things inside my queries.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1507925921958-8a62f3d1a50d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&amp;amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA&quot; alt=&quot;Macro typewriter ribbon&quot; /&gt;

&lt;figcaption&gt;&lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@kellysikkema?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Kelly Sikkema&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Things were worse with long store procedures. I changed two lines and ended up formatting thousand of lines. &lt;em&gt;“Once you touch it, you’re the owner.”&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;lets-format-a-sample-query-from-stackoverflow&quot;&gt;Let’s format a sample query from StackOverflow&lt;/h2&gt;

&lt;p&gt;Let’s format the query to &lt;a href=&quot;https://data.stackexchange.com/stackoverflow/query/886/posts-with-many-thank-you-answers&quot;&gt;find StackOverflow posts with many “thank you” answers&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;ParentId&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posttypeid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;like&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%hank%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentid&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;having&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After formatting the query to follow &lt;a href=&quot;https://www.sqlstyle.guide/&quot;&gt;Simon Holywell SQL Style Guide&lt;/a&gt;, it should look like this,&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParentId&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posttypeid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;%hank%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parentid&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;HAVING&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s see how these two tools format our sample query.&lt;/p&gt;

&lt;h3 id=&quot;1-poor-mans-t-sql-formatter&quot;&gt;1. Poor Man’s T-SQL Formatter&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/TaoK/PoorMansTSqlFormatter&quot;&gt;Poor Man’s T-SQL Formatter&lt;/a&gt; is a free and open-source .NET and JavaScript library to format your SQL queries. It’s available for Notepad++, Visual Studio, SQL Server Management Studio, and others. We can &lt;a href=&quot;http://poorsql.com/&quot;&gt;try it online&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;This is how Poor Man’s T-SQL formatted our sample query in Notepad++.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-09-30-FormatSQL/PoorManTSQL.PNG&quot; alt=&quot;Sample query formatted by Poor Man&apos;s T-SQL inside Notepad++&quot; width=&quot;700px&quot; /&gt;
    &lt;figcaption&gt;Sample query formatted by Poor Man&apos;s T-SQL inside Notepad++&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It doesn’t make function names uppercase. Notice the functions &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;len&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, it indents &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AND&lt;/code&gt; clauses in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; clause. I want them right-aligned to the previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt;. But it’s a good starting point.&lt;/p&gt;

&lt;p&gt;Sometimes, it needs a bit of help if the query has single-line comments in it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By the way, it’s better to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/* */&lt;/code&gt; for single-line comments inside our queries and store procedures. This makes formatting easier when we copy queries or statements from our database’s plan cache.&lt;/p&gt;

&lt;h3 id=&quot;2-apexsql-refactor&quot;&gt;2. ApexSQL Refactor&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.apexsql.com/sql-tools-refactor.aspx&quot;&gt;ApexSQL Refactor&lt;/a&gt; is a &lt;del&gt;free&lt;/del&gt; query formatter for Visual Studio and SQL Server Management Studio. It has over 160 formatting options. We can create our own formatting profiles and preview them. It comes with four built-in profiles. &lt;del&gt;Also, we can try it online&lt;/del&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;UPDATE (Sept 2023)&lt;/strong&gt;: ApexSQL Refactor isn’t freely available online anymore.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is how ApexSQL Refactor formatted our sample query in Visual Studio 2019.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-09-30-FormatSQL/ApexSQL.PNG&quot; alt=&quot;Sample query formatted by ApexSQL Refactor inside Visual Studio&quot; width=&quot;600px&quot; /&gt;
    &lt;figcaption&gt;Sample query formatted by ApexSQL Refactor inside Visual Studio&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It isn’t perfect, either. But, it makes functions uppercase. Point for ApexSQL Refactor.&lt;/p&gt;

&lt;p&gt;Also, it indents &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AND&lt;/code&gt; clauses in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; too. I couldn’t find an option to change it. But, there is an option to indent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ON&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT&lt;/code&gt; statements with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JOIN&lt;/code&gt;. It affects &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ON&lt;/code&gt; for index creation too. We can live with that.&lt;/p&gt;

&lt;p&gt;Voilà! Please let’s save some time formatting our SQL queries with any of these two free tools.&lt;/p&gt;

&lt;p&gt;We can take a step further and call a formatter inside a Git hook to &lt;a href=&quot;/2023/09/18/FormatSqlFilesOnCommit/&quot;&gt;automatically format SQL files&lt;/a&gt;. I did it with Poor Man’s T-SQL formatter.&lt;/p&gt;

&lt;p&gt;For more content, check &lt;a href=&quot;/2019/06/28/MyVSSetupSharpeningTheAxe/&quot;&gt;my Visual Studio setup for C#&lt;/a&gt; and &lt;a href=&quot;/2020/09/28/SQLServerTuningTips/&quot;&gt;six tips to performance tune our SQL Server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy SQL time!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Six SQL Server performance tuning tips from Pinal Dave</title>
   <link href="https://canro91.github.io/2020/09/28/SQLServerTuningTips/"/>
   <updated>2020-09-28T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/09/28/SQLServerTuningTips</id>
   <content type="html">&lt;p&gt;Recently, I’ve needed to optimize some SQL Server queries. I decided to look out there what to do to tune SQL Server and SQL queries. This is what I found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At the database level, turn on automatic update of statistics, increase the file size autogrowth and update the compatibility level. At the table level, delete unused indexes and create the missing ones, keeping around 5 indexes per table. And, at the query level, find and fix implicit conversions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While looking up what I could do to tune my queries, I found &lt;a href=&quot;https://blog.sqlauthority.com/&quot;&gt;Pinal Dave from SQLAuthority&lt;/a&gt;. Chances are you have already found one of his blog posts when searching for SQL Server tuning tips. He’s been blogging about the subject for years.&lt;/p&gt;

&lt;p&gt;These are six tips from Pinal’s blog and online presentations I’ve applied recently. Please, let’s test these changes in a development or staging environment before making anything on our production servers.&lt;/p&gt;

&lt;h2 id=&quot;1-enable-automatic-update-of-statistics&quot;&gt;1. Enable automatic update of statistics&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Let’s turn on automatic update of statistics.&lt;/strong&gt; We should turn it off if we’re updating a really long table during your work-hours.&lt;/p&gt;

&lt;p&gt;This is how to enable automatic update of statistic update, &lt;a href=&quot;https://blog.sqlauthority.com/2009/10/15/sql-server-enable-automatic-statistic-update-on-database/&quot;&gt;[Source]&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YourDatabase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- Enable Auto Create of Statistics&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YourDatabase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_CREATE_STATISTICS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO_WAIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- Enable Auto Update of Statistics&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YourDatabase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_UPDATE_STATISTICS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO_WAIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;-- Update Statistics for whole database&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;EXEC&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sp_updatestats&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;2-fix-file-autogrowth&quot;&gt;2. Fix File Autogrowth&lt;/h2&gt;

&lt;p&gt;Let’s add size and file growth to our database. Let’s use our weekly file growth. Otherwise, let’s change it to 200 or 250MB.&lt;/p&gt;

&lt;p&gt;From SQL Server Management Studio, to change the file autogrowth:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Let’s go to our database properties and then to Files, then&lt;/li&gt;
  &lt;li&gt;Click on the three dots in the Autogrowth column, and&lt;/li&gt;
  &lt;li&gt;Change the file growth.&lt;/li&gt;
&lt;/ol&gt;

&lt;figure&gt;
    &lt;img src=&quot;/assets/posts/2020-09-28-SQLServerTuningTips/FileAutogrowth.png&quot; alt=&quot;Files page from Database properties in SQL Server Management Studio&quot; width=&quot;800px&quot; /&gt;
    &lt;figcaption&gt;Files page from Database properties in SQL Server Management Studio&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;3-find-and-fix-implicit-conversions&quot;&gt;3. Find and Fix Implicit conversions&lt;/h2&gt;

&lt;p&gt;Implicit conversions happen when SQL Server needs to convert between two data types in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; or in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JOIN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if we compare a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderNumber&lt;/code&gt; column being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VARCHAR(20)&lt;/code&gt; to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INT&lt;/code&gt; parameter, SQL Server warns about an implicit conversion.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrderNumber&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Orders&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OrderNumber&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrderNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To run this query, SQL Server has to go through all the rows in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Orders&lt;/code&gt; table to convert the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrderNumber&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VARCHAR(20)&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To decide when implicit conversions happen, SQL Server follows a precedence rule between data types. For example, SQL Server always converts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VARCHAR&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NVARCHAR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This script indentifies queries with implicit conversions, &lt;a href=&quot;https://blog.sqlauthority.com/2018/06/11/sql-server-how-to-fix-convert_implicit-warnings/&quot;&gt;[Source]&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Database&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_worker_time&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Total&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Worker&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_worker_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execution_count&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Avg&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Worker&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_worker_time&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Max&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Worker&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_elapsed_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execution_count&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Avg&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_elapsed_time&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Max&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Elapsed&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_logical_reads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execution_count&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Avg&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Logical&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Reads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_logical_reads&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Max&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Logical&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Reads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execution_count&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Execution&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;creation_time&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Creation&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;qp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query_plan&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Plan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_exec_query_stats&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOLOCK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CROSS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;APPLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_exec_sql_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plan_handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;CROSS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;APPLY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_exec_query_plan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plan_handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qp&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query_plan&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NVARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MAX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%CONVERT_IMPLICIT%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_worker_time&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OPTION&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RECOMPILE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s be aware, not all implicit convesions are bad. Often implicit conversions lead to scans or seeks. That’s why &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;we should care about implicit conversions&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/ef-BmyNipU4?start=196&amp;amp;rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;4-change-compatibility-level&quot;&gt;4. Change compatibility level&lt;/h2&gt;

&lt;p&gt;After updating our SQL Server, let’s make sure to update the compatibility level of our database to the highest level supported by the current version of our SQL Server.&lt;/p&gt;

&lt;p&gt;We can change your SQL Server compatibility level using SQL Server Management Studio or with  a query. &lt;a href=&quot;https://blog.sqlauthority.com/2017/05/22/sql-server-change-database-compatibility-level/&quot;&gt;[Source]&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;ALTER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YourDatabase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;COMPATIBILITY_LEVEL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;150&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;130&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;110&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-find-and-create-missing-indexes&quot;&gt;5. Find and Create missing indexes&lt;/h2&gt;

&lt;p&gt;Let’s create our missing indexes. But, let’s not create them all. Let’s create the first 10 missing indexes in our database and stick to around 5 indexes per table.&lt;/p&gt;

&lt;p&gt;We can use the next script to find the missing indexes in our database. &lt;a href=&quot;https://blog.sqlauthority.com/2011/01/03/sql-server-2008-missing-index-script-download/&quot;&gt;[Source]&lt;/a&gt; Let’s check the indexes we already have and the estimated impact of the missing indexes. &lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;Let’s not blindly follow index recommendations&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DatabaseID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dm_migs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;avg_user_impact&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_migs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_seeks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_migs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_scans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Avg_Estimated_Impact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dm_migs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_user_seek&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Last_User_Seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OBJECT_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TableName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;&apos;CREATE INDEX [IX_&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OBJECT_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;_&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;equality_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;, &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;_&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;[&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;]&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;equality_columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inequality_columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;_&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;REPLACE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inequality_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;, &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;_&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;[&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;]&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;]&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos; ON &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;statement&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos; (&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;equality_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;equality_columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inequality_columns&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inequality_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;)&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ISNULL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; INCLUDE (&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;included_columns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;)&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create_Statement&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_db_missing_index_groups&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mig&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_db_missing_index_group_stats&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_migs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_migs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_handle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_group_handle&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_db_missing_index_details&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_handle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_handle&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Avg_Estimated_Impact&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/fX05yEkSkpo?start=706&amp;amp;rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;6-delete-unused-indexes&quot;&gt;6. Delete unused indexes&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Indexes reduce perfomance all the time.&lt;/strong&gt; They reduce performance of inserts, updates, deletes and selects. Even if a query isn’t using an index, it reduces performance of the query.&lt;/p&gt;

&lt;p&gt;Let’s delete most our indexes. Let’s identify our “main” table and check if it has more than 5 indexes.&lt;/p&gt;

&lt;p&gt;Also, let’s keep in mind if we rebuild an index for a table, SQL Server will remove all plans cached for that table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rebuilding indexes is the most expensive way of updating statistics.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;video-container&quot;&gt;
&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/SqhX8OaOI6A?start=395&amp;amp;rel=0&amp;amp;fs=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;We can find our unused indexes with the next script. &lt;a href=&quot;https://blog.sqlauthority.com/2011/01/04/sql-server-2008-unused-index-script-download/&quot;&gt;[Source]&lt;/a&gt; Let’s look for indexes with zero seeks or scans and lots of updates. They’re good candidates to drop.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TOP&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObjectName&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IndexName&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IndexID&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_seeks&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserSeek&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_scans&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserScans&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_lookups&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserLookups&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_updates&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserUpdates&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TableRows&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;DROP INDEX &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QUOTENAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos; ON &apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QUOTENAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;.&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QUOTENAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;drop statement&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_db_index_usage_stats&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_id&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schemas&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schema_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schema_id&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SUM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TableRows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;partitions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OBJECTPROPERTY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OBJECT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;IsUserTable&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DB_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type_desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;nonclustered&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_primary_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_unique_constraint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_seeks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_scans&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dm_ius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_lookups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ASC&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! These are six tips I learned from Pinal Dave to start tuning your SQL Server. Let’s pay attention to your implicit conversions. You can get a surprise.&lt;/p&gt;

&lt;p&gt;I gained a lot of improvement only by fixing implicit conversions. In a store procedure, we had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NVARCHAR&lt;/code&gt; parameter to compare it with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VARCHAR&lt;/code&gt; column. Yes, implicit conversions happen between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VARCHAR&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NVARCHAR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After taking &lt;a href=&quot;/2022/05/02/BrentOzarMasteringCoursesReview/&quot;&gt;Brent Ozar’s Mastering courses&lt;/a&gt;, I learned to look at the overall SQL Server server health, focusing on the top wait type and the most expensive queries. Also, I started to use some of the stored procedures from the &lt;a href=&quot;https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit&quot;&gt;First Responder Kit repository on GitHub&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;For more SQL and performance tuning content, check &lt;a href=&quot;/2022/01/24/DontPutFunctionsInYourWheres/&quot;&gt;don’t use functions around columns in your WHEREs&lt;/a&gt;, &lt;a href=&quot;/2022/02/07/WhatAreImplicitConversions/&quot;&gt;what implicit conversions are and why you should care&lt;/a&gt; and &lt;a href=&quot;/2022/03/21/SQLServerIndexRecommendations/&quot;&gt;just listen to index recommendations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy SQL time!&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>BugOfTheDay: The slow room search</title>
   <link href="https://canro91.github.io/2020/09/23/TheSlowRoomSearch/"/>
   <updated>2020-09-23T00:00:00+00:00</updated>
   <id>https://canro91.github.io/2020/09/23/TheSlowRoomSearch</id>
   <content type="html">&lt;p&gt;Another day at work! This time, the room search was running slow. For one of the big hotels, searching all available rooms in a week took about 15 seconds. This is how I optimized the room search functionality.&lt;/p&gt;

&lt;p&gt;This room search was a public page to book a room into a hotel without using any external booking system. This page was like a custom Booking.com page. This page used an ASP.NET Core API project to combine data from different internal microservices to display the calendar, room images, and prices of a hotel.&lt;/p&gt;

&lt;h2 id=&quot;1-room-type-details&quot;&gt;1. Room type details&lt;/h2&gt;

&lt;p&gt;At first glance, I found an N+1 query problem. This is a common anti-pattern. The code called the database per each element from an input set to find more details about each item.&lt;/p&gt;

&lt;p&gt;This N+1 problem was in the code to find the details of each room type. The code looked something like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomTypeViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRoomTypesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypeIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypeIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roomTypeId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRoomTypeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                      ^^^^^&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WhenAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomTypeViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRoomTypeAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomClass&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_apiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetJsonAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomTypeViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//                               ^^^^^&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These two methods made a request per each room type found, instead of searching more than one room type in a single call. I decided to create a new method to receive a list of room types.&lt;/p&gt;

&lt;p&gt;I cloned the existing method in the appropriate microservice and renamed it. The new method received an array of room types. Also, I removed all unneeded queries from the store procedure it used. The store procedure returned three results sets. But, the room search only cared about one.&lt;/p&gt;

&lt;p&gt;Before any change, it took ~4 seconds to find a single room type. But, with the new method, it took ~600ms to find more than one room type in a single request. The hotel facing the problem had about 30 different room types. Hurray!&lt;/p&gt;

&lt;p&gt;After this first change, the room search made a single request to find all room types.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomTypeViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRoomTypesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypeIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BuildEndpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_apiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PostJsonAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomTypeViewModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;roomTypeIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, when calling the room search from Postman, the execution time didn’t seem to improve at all. These were some of the times for the room search for one week. What went wrong?&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Room Type&lt;/th&gt;
      &lt;th&gt;Time in seconds&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Before&lt;/td&gt;
      &lt;td&gt;16.95 18.39 17.13&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;After&lt;/td&gt;
      &lt;td&gt;19.48 17.61 18.65&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;figure&gt;
&lt;img src=&quot;https://images.unsplash.com/photo-1445019980597-93fa8acb246c?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=crop&amp;amp;fm=jpg&amp;amp;h=400&amp;amp;ixlib=rb-4.0.3&amp;amp;q=80&amp;amp;w=600&amp;amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA&quot; width=&quot;600&quot; /&gt;

&lt;figcaption&gt;Encuentro Guadalupe, El Porvenir, Mexico. &lt;span&gt;Photo by &lt;a href=&quot;https://unsplash.com/@manuelmx?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Manuel Moreno&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/hotel?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;2-premature-optimization&quot;&gt;2. Premature optimization&lt;/h2&gt;

&lt;p&gt;To check what happened, I stepped back and went to the room search method again. The room search method looked for all available rooms, the best rates and any restrictions to book a room. This method was something like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Authorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RoomSearchAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoomSearchRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;   
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hotelTimeZone&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHotelTimeZoneAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* some parameters */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomListTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRoomListAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* some parameters */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ratesTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRatesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* some parameters */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rulesTask&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRulesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* some parameters */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WhenAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roomListTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ratesTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyRulesTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomListTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ratesTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rules&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rulesTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomSearchResults&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PrepareRoomSearchResultsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* some parameters */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RoomSearchResponse&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Rooms&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomSearchResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Rules&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rules&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To find any bottlenecks, I wrapped some parts of the code using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stopwatch&lt;/code&gt; class and log the elapsed time of them.&lt;/p&gt;

&lt;p&gt;These are the log messages with the execution times before any change looked like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GetHotelTimeZoneAsync: 486ms
Task.WhenAll: 9641ms
    GetRatesAsync: 9588ms
    GetRoomListAsync: 7008ms
        FindAvailableRoomsAsync: 2792ms
        GetRoomTypesAsync:       4204ms
                                 ^^^^^
    GetRulesAsync: 3030ms
GetRoomTypeGaleriesAsync: 8228ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, the log after using the new method for room type details showed why it didn’t seem to improve. The log looked like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GetHotelTimeZoneAsync: 616ms
Task.WhenAll: 8726ms
    GetRatesAsync: 8667ms
    GetRoomListAsync: 4171ms
        FindAvailableRoomsAsync: 3602ms
        GetRoomTypesAsync:        559ms
                                  ^^^^^
    GetRulesAsync: 4223ms
GetRoomTypeGaleriesAsync: 11486ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetRoomTypesAsync&lt;/code&gt; method run concurrently next to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetRatesAsync&lt;/code&gt; method. This last one was the slowest of the three methods inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task.WhenAll&lt;/code&gt;. That’s why there was no noticeable improvement even though the time of the room type call dropped from 4204ms to 559ms.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Premature optimization is the root of all evil”&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;-Donald Knuth&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was looking at the wrong place! I rushed to optimize without measuring anything. Lesson learned! I needed to start working either on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHotelTimeZoneAsync&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetRoomTypeGaleriesAsync&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;3-room-gallery&quot;&gt;3. Room gallery&lt;/h2&gt;

&lt;p&gt;This time to gain noticeable improvement, I moved to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetRoomTypeGaleriesAsync&lt;/code&gt; method. Again, this method called another microservice. The code looked like this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomGallery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRoomGalleriesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomTypeIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomGalleries&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_roomRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetRoomImagesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roomTypeIds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roomGalleries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomGallery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomGalleries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomGalleriesTasks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomGalleries&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rg&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ^^^^^&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MapToRoomGallery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RoomTypeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hotelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roomGalleries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WhenAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roomGalleriesTasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AsEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&g