14 Feb 2025 #books #writing
Like coding, writing is everywhere in software projects.
From README files to use cases to bug reports to blog posts. We developers spend as much time writing as coding, if not more.
Chances are if you’ve been stuck with an error for hours, some random guy’s blog post has saved your day.
Writing for Developers shows how to write those technical (or engineering) blog posts.
Here’s my honest review and key takeaways:
“You’re not writing enough” and other takeaways
#1. Technical blog posts don’t have to be boring.
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.
#2. If you find a subject interesting, other people will find it too.
Write about anything you find interesting.
But if you’re writing for the first time and you’re not sure what to write about, remember 3 Ps:
- what you’re passionate about
- what you’re pained by
- what you’re proud of
#3. We shouldn’t rely on AI to write our posts.
But, it doesn’t mean we can’t use it to critique or review our drafts.
Here are two prompts to critique our drafts:
- “Are there any statements in this engineering blog post that do not seem adequately supported by facts?”
- “Can you identify any parts of this article that seem less relevant to its main goal of [your goal]?”
My favorite lesson? “You’re not writing enough.” You can always write more.
My alternative “how to read this book”
Writing is not a subject you will learn from a single book.
It takes years to master it. And we, as developers, have a double challenge: putting words together and presenting complex subjects clearly.
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.
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.
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.
Part 3 covers 7 blog post patterns:
- Bug Hunt
- Rewrote it in X
- How We Built it
- Lessons Learned
- Thoughts on Trends
- Non-markety Product Perspectives
- Benchmarks and Test results
Each chapter contains sample blog posts, a commentary on those posts, and an explanation of how to write each type of post.
That was my favorite part.
Definitely I’ve written some of those type of posts. But I didn’t think of them as patterns we could implement.
My final verdict? 3.8/5.
13 Feb 2025 #csharp #todayilearned
Oh boy! AutoMapper once again.
Today I have CreateMovieRequest
with a boolean property ICanWatchItWithKids
that I want to map to a MPA rating. If I can watch it with kids, the property MPARating
on the destination type should get a “General” rating. Anything else gets “Restricted.”
To my surprise, this test fails:
using AutoMapper;
namespace TestProject1;
[TestClass]
public class WhyAutoMapperWhy
{
public class CreateMovieRequest
{
public string Name { get; set; }
public bool ICanWatchItWithKids { get; set; }
}
public class Movie
{
public string Name { get; set;}
public MPARating Rating { get; set;}
}
public enum MPARating
{
// Sure, there are more.
// But these two are enough.
General,
Restricted
}
[TestMethod]
public void AutoMapperConfig_IsValid()
{
var mapperConfig = new MapperConfiguration(options =>
{
options.CreateMap<CreateMovieRequest, Movie>(MemberList.Source)
.ForMember(
dest => dest.Rating,
opt => opt.MapFrom(src => src.ICanWatchItWithKids
? MPARating.General
: MPARating.Restricted));
});
mapperConfig.AssertConfigurationIsValid();
// ^^^^^
// AutoMapper.AutoMapperConfigurationException:
// CreateMovieRequest -> Movie (Source member list)
// TestProject1.WhyAutoMapperWhy+CreateMovieRequest -> TestProject1.WhyAutoMapperWhy+Movie (Source member list)
//
// Unmapped properties:
// ICanWatchItWithKids
}
}
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 Upgrade Guide here. Arrggg!
Two solutions: One for the lazy and the right one
Since I’m validating mappings based on the source type, I can simply ignore it:
options.CreateMap<CreateMovieRequest, Movie>(MemberList.Source)
.ForMember(
dest => dest.Rating,
opt => opt.MapFrom(src => src.ICanWatchItWithKids ? MPARating.General : MPARating.Restricted))
.ForSourceMember(src => src.ICanWatchItWithKids, opt => opt.DoNotValidate());
// ^^^^^
// Thanks AutoMapper, I'll take it from here.
It feels like cheating, but it works.
Or, I can use a converter:
options.CreateMap<CreateMovieRequest, Movie>(MemberList.Source)
.ForMember(
dest => dest.Rating,
opt => opt.ConvertUsing(
// ^^^^^
new FromBoolToMPARating(),
src => src.ICanWatchItWithKids));
// And here's the converter:
public class FromBoolToMPARating : IValueConverter<bool, MPARating>
{
public MPARating Convert(bool sourceMember, ResolutionContext context)
{
// Here's the actual mapping:
return sourceMember ? MPARating.General : MPARating.Restricted;
}
}
Another day working with AutoMapper. It would have been way easier mapping that by hand.
For more adventures with AutoMapper, check How to ignore unmapped fields in the destination type.
12 Feb 2025 #misc
A bit of nostalgia, reflection, and compilation today.
This is what happened, not today, but around this day in my blog:
Last year, before being laid off, 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 two lessons I learned from that project.
In 2023, I started a 4-part series on NullReferenceException with a post about when NullReferenceException is thrown and how to avoid it.
In 2022, I was still running Monday Links here. I wrote about Daily Meetings, Estimates, and Challenges. From that episode, I included a post I found with 5 signs to look for when it’s time to quit your job.
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. Join here.
In 2021, I was working with a Stripe-powered payment processor. And I wrote about how to use the Decorator pattern to implement retry logic when sending payments to Stripe. The analogy I used to explain the Decorator pattern was IronMan wearing the HULKBUSTER suit.
In 2020, I used the Pipeline pattern 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.
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.
11 Feb 2025 #coding
Rewriting legacy code that works isn’t a good idea.
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.
These days, Antirez, Redis’ creator, wrote in We are destroying software:
We are destroying software pushing for rewrites of things that work.
Rewrites are expensive and time-consuming. Every rewrite starts with good intentions, but they all end up being the next application to rewrite.
Interestingly enough, I’ve been involved in full rewrites at every job I’ve had.
At my first job, 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, I was fired), there was an ongoing project to unify every separate department application into a single “unified” platform. Another rewrite.
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.
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 use Hangfire for that.
Next job? Rewriting a VisualBasic WebForm app into Blazor. Oh boy!
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.
10 Feb 2025 #misc
To stand out, do what everybody else isn’t doing.
That’s a lesson I learned from Isra Bravo, Spain’s top copywriter, and maybe the country’s top solopreneur.
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.
Recently, I discovered Keygen, a licensing software and its no calls policy.
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.
Instead of sales calls, they improved their messaging. They clarified what their tool does and how much it costs.
And when someone requests a sales call, they simply reply:
“No, we don’t do calls, but happy to help via email. Feel free to CC any relevant team members onto this thread.”
Some of Keygen’s prospects loved the #nocalls policy. Too many calls are boring, draining, and time-consuming.
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.
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.
#nocalls is an idea worth copying and stealing, especially if selling feels draining and if you prefer to think of in terms of helping, not selling. Better messaging = more sales.