Follow This Proven Strategy to End Scope Creep (While Keeping Clients Happy)

There’s a thin line between making your clients happy and doing everything they want.

Derek Sivers in Anything You Want shares his strategy to end “small favors” while keeping clients happy.

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

A pizza isn’t free but generates small friction to end next favors.

Replace pizzas with coffee or hamburgers. Also great idea to also support local businesses.

TIL: How to Read Invalid Fields as Null with CsvHelper

In another episode of the Excel paradox of Coding

TL;DR: To read an invalid field from a CSV file as null with CsvHelper, create a map class and use Default() with useOnConversionFailure set to true on the appropriate field.

The cleanest approach

To honor the 20-minute rule and preserve my keystrokes, 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.

Here’s how to read that invalid field as null instead of throwing an exception,

using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;
using System.Text;

namespace TestProject1;

[TestClass]
public class CsvHelperTests
{
    private class MovieRating
    {
        public string Name { get; set; }
        public float? Rating { get; set; }
    }

    private sealed class MovieRatingMap : ClassMap<MovieRating>
    {
        public MovieRatingMap()
        {
            Map(m => m.Name);
            Map(m => m.Rating)
                .Default(defaultValue: default(float?),
                         useOnConversionFailure: true);
            //           ^^^^^
            // If the field is invalid, use the default value
        }
    }

    [TestMethod]
    public void NullWhenInvalidFloats()
    {
        var csv = new StringBuilder()
            .AppendLine("Name,Rating")
            .AppendLine("Inception,9.0")
            .AppendLine("Titanic,ThisIsNotAValidRating")  /* <-- */
            .ToString();

        using var reader = new StringReader(csv);
        using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture);
        csvReader.Context.RegisterClassMap<MovieRatingMap>();
        //                ^^^^^

        var records = csvReader.GetRecords<MovieRating>().ToList();

        Assert.AreEqual(2, records.Count);

        var last = records.Last();
        Assert.AreEqual("Titanic", last.Name);
        Assert.IsNull(last.Rating);
        //            ^^^^^
        // Look, ma! It's null
    }
}

The magic happens in MovieRatingMap. The Rating map uses Default() with useOnConversionFailure set to true.

The manual approach

As an alternative, let’s read the CSV file manually while processing the records as needed,

[TestMethod]
public void ByHand()
{
	var csv = new StringBuilder()
		.AppendLine("Name,Rating")
		.AppendLine("Inception,9.0")
		.AppendLine("Titanic,ThisIsNotAValidRating")
		.ToString();

	using var reader = new StringReader(csv);
	using var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture);
	csvReader.Context.RegisterClassMap<MovieRatingMap>();

	csvReader.Read();
	csvReader.ReadHeader();
	//        ^^^^^
	// Open the reader and read the header

	var records = new List<MovieRating>();
	while (csvReader.Read())
	//               ^^^^^
	// Read it by hand...
	{
		var name = csvReader.GetField<string>("Name");
		var rating = csvReader.GetField("Rating");
		//                     ^^^^^
		// Read the field as string...

		var parsedRating = !float.TryParse(rating, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed)
								? default(float?)
								: parsed;
		// Parse it as needed...

		records.Add(new MovieRating
		{
			Name = name,
			Rating = parsedRating
		});
	}

	Assert.AreEqual(2, records.Count);

	var last = records.Last();
	Assert.AreEqual("Titanic", last.Name);
	Assert.IsNull(last.Rating);
	//            ^^^^^
	// Look, ma! It's still null
}

A lot more work! We have full control, but it’s less clean.

In case you’re wondering, I asked Copilot about this but, with a straight face, it hallucinated suggesting a flag on CsvConfiguration, which didn’t even exist.

For scenarios like this, you still need strong debugging and code reading skills. I cover those skills and more in Street-Smart Coding—A roadmap with 30 lessons to help you code like a pro.

Friday Links: Jesus on prototyping, C# union types, and taste

Hey there.

Here are 4 links I thought were worth sharing this week:

#1. After a long wait, C# union types are here (10min). Well, in the next C# version. If you’re new to union types, I wrote this guide (9min) when Microsoft announced the feature.

#2. If you’re new to a codebase, run these Git commands (5min) before reading any code. Treat it like a crime scene: gather evidence before touching anything.

#3. When AI makes code and text free, the only skills left are taste and judgment (10min). I liked the exercise of “fails because.”

#4. Did you know that Jesus taught about prototyping with an LLM? Here’s his advice (3min).


And in case you missed it, not much about coding on my blog, but I wrote about the secret to surviving 30 years in one job (and to live a happy life) (3min).


(Bzzz…Radio voice) This email was brought to you by… Street-Smart Coding, 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.

See you next Friday with more links.

Keep coding smartly.

Cesar

The Real Effect of a Daily Writing Habit

My first writing class shocked me.

Its main lesson was to write every day. It was a webinar pitching a writing course. But it made me revive my blog after abandoning it for months.

Since November 1st, 2024, I’ve been writing daily. If I miss a day, I write two posts to recover the streak. More than once I’ve thought I’d run out of ideas.

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

As Robert Greene says, “it’s all material.” A conversation could be a mini-story. A failed delivery, a business lesson. A visit to the grocery store, a life lesson.

Writing daily is a habit that has truly changed my life.

Write Not to Be Read (Yes, You Read That Right)

Almost nobody reads online.

We don’t deserve attention, especially when content is free and abundant.

Smart Brevity says we have a few milliseconds to earn attention:

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

Christopher Butler writes in Earning Attention,

“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.”

Write as if nobody is reading, but everybody is skimming.