The 3 Best Pieces of Unsolicited Advice I've Ever Received

You know that saying, “Don’t give advice unless asked?”

Well, some people ignored it and gave it anyway. Here are the best pieces of advice I’ve received:

From high school:

“Read, because if you don’t read, you won’t learn anything.”

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.

If I still remember that years later, he did his job.

From my first job:

“Imagine you make half of your salary, save the other half.”

A senior co-worker at my first coding job gave me that totally unexpectedly. That lesson stuck with me so much, I couldn’t resist adding it to my an email course I created years later.

From my last full-time job:

“Diversify your career.”

That was from one of my not-mentors. I reached out to him when I wasn’t sure about where to take my software engineering career. We had many 1-on-1s. That’s what I remember the most. It was good advice. I didn’t follow it. I burned out.

A Quick Recovery Guide for AI-Dependent Coders

Technology makes us lazy.

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.

The same thing happens in coding, with AI and vibe-coding.

I’m guilty too. I’ve been experimenting with AI 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.

And I’m not alone in this. Recently, I found this question on Reddit,

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

For the original poster and anyone else who wants to break free from AI, here are 10 ideas to try:

0. Ban AI.

Think of AI as calculators in math classes. You can’t use them until you know the procedure you want to automate by hand.

Like any mom disciplining her kid, “No more AI until you do your homework…”

1. Study your main language syntax.

Get to know the syntax of your language of choice: write variables, functions, loops, classes…

For that, grab a textbook or watch a “all you need to know about X in 4 hours” YouTube videos. But don’t just passively consume them, recreate the examples and projects from them. By typing them out. No Control C + Control V.

2. Know your standard library.

Get familiar with your standard library:

  • Write a variable and see what your editor or IDE suggests.
  • What methods can you use with that type?
  • Look at their signature and docstring.

3. Study SQL.

No matter how powerful ORMs are, we can’t escape from SQL.

We’ve had SQL since the early 70s and chances are we’re still using SQL another decade or two.

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.

4. Build a toy project from scratch.

OK, I’m not talking about reinventing the wheel to write your own text editor or something.

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.

5. Find your own answers.

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.

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)

6. Learn the most common data structures.

80% of the time, you’ll only need lists and dictionaries. But there are more data structures of course.

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.

7. Study a textbook on Math for Computer Science.

Unless you’re working on niche domains, you won’t need advanced Math.

But grab a book on Discrete Math (or Math for Computer Science) and study a chapter or two. Again, to sharpen your thinking.

8. Practice rubber-duck debugging.

You will get stuck a lot. That’s a feature of being a coder, not a bug.

When that happens,

  1. Grab pen and paper
  2. Go through your program line by line
  3. Talk out loud

9. Read the official documentation.

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.

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, don’t use it to generate code. Use it as your copilot, not as your captain.

If you’re new at coding, stop chasing shiny objects and follow these 10 tips every new coder should know to succeed.

Friday Links: Negativity, Git Notes, and hating your manager

Hey there.

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

#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 this developer’s best career advice (4min)

#2. I have never used Git Notes. Yes, that’s a Git feature. But it seems to be the coolest and most unloved Git feature (5min)

#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 why we engineers (sometimes) hate our managers (7min).

#4. Despite all Internet advice about surviving the AI trend, we should stop getting good at ChatGPT (20min). Because to get “good” at using ChatGPT to execute your tradecraft is thus to get good at commoditizing your own labor and your craft.


And in case you missed it, I wrote on my blog about how to split an array into odd pairs (an interview exercise) (6min) and some ideas to avoid being ghosted when emailing busy people (3min).


(Bzzz…Radio voice) This email was brought to you by… Check my Gumroad store to access free and premium books and courses to level up your coding skills and grow your software engineering career.

See you next time,

Cesar

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. Subscribe to my Friday Links here.

How to Split an Array Into Odd Pairs (An Interview Exercise)

Here’s an exercise that I may or may not have found… or been asked to solve.

Off the top of my head, the exercise might or might not have looked like this,

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.

For example, [1, 2, 3, 4] passes those two conditions. Here are its pairs: (1, 2) and (3, 4). Their sum is an odd number.

But [1, 3, 5, 7] doesn’t pass our two conditions. Each pair adds up to an even number.

Cheating with StackOverflow and Copilot

Let’s create a producer-like method to generate all pairs, and then filter them out based on our two conditions.

I’d like to write something like this,

bool HasFunnyPairs(int[] array)
    => array.Pais()
        .Where(pair => IsOdd(Sum(pair)))
        .Combinations(array.Length / 2)
        .Any(combination => IsEachElementInExactlyOnePair(array, combination));

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.

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.

Generating k-Combinations

Here’s a StackOverflow answer with a recursive method in JavaScript to choose k-combinations of an array,

function choose(arr, k, prefix=[]) {
    if (k == 0) return [prefix];
    return arr.flatMap((v, i) =>
        choose(arr.slice(i+1), k-1, [...prefix, v])
    );
}

I asked Copilot to translate it to C#. After some tweaking, we have this,

static IEnumerable<IEnumerable<T>> Combinations<T>(IEnumerable<T> source, int size)
{
	return CombinationsInternal(source, size, null);

	static IEnumerable<IEnumerable<T>> CombinationsInternal(IEnumerable<T> source, int size, List<T>? prefix = null)
	{
		prefix ??= [];

		if (size == 0)
		{
			yield return new List<T>(prefix);
			yield break;
		}

		var length = source.Count();
		foreach ((T element, int position) in source.Select((element, position) => (element, position)))
		{
			List<T> newPrefix = [.. prefix, element];
			foreach (var combination in CombinationsInternal([.. source.Skip(position + 1)], size - 1, newPrefix))
			{
				yield return combination;
			}
		}
	}
}

With that method, we can write Pairs() to generate a list of tuples, representing each pair,

static IEnumerable<(int First, int Second)> Pairs(this int[] array)
    => array.Combinations(2)
            .Select(pair => (First: pair.First(), Second: pair.Last()));

Finding if each element is present in only one pair

The methods IsOdd() and Sum() are easy. The one left is IsEachElementInExactlyOnePair(). Copilot to the rescue again,

static bool IsEachElementInExactlyOnePair(int[] elements, IEnumerable<(int First, int Second)> pairs)
{
    var elementCount = new Dictionary<int, int>();

    // Count occurrences of elements in pairs
    foreach (var (First, Second) in pairs)
    {
        elementCount[First] = elementCount.GetValueOrDefault(First) + 1;
        elementCount[Second] = elementCount.GetValueOrDefault(Second) + 1;
    }

    // Check if each element appears exactly once
    return elements.All(e => elementCount.GetValueOrDefault(e) == 1)
           && elements.All(e => elementCount.ContainsKey(e));
}

Now, after some gluing and turning some of those methods into extension methods, here’s the final solution,

On a Helpers.cs file,

namespace HereIGo;

public static class Helpers
{
    public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> array, int size)
    {
        return CombinationsInternal(array, size, null);

        static IEnumerable<IEnumerable<T>> CombinationsInternal(IEnumerable<T> array, int size, List<T>? prefix = null)
        {
            prefix ??= [];

            if (size == 0)
            {
                yield return new List<T>(prefix);
                yield break;
            }

            var length = array.Count();
            foreach ((T element, int position) in array.Select((element, position) => (element, position)))
            {
                List<T> newPrefix = [.. prefix, element];
                foreach (var combination in CombinationsInternal([.. array.Skip(position + 1)], size - 1, newPrefix))
                {
                    yield return combination;
                }
            }
        }
    }

    public static IEnumerable<(int First, int Second)> Pairs(this int[] array)
        => array.Combinations(2)
            .Select(pair => (First: pair.First(), Second: pair.Last()));

    public static int Sum(this (int First, int Second) pair) => pair.First + pair.Second;

    public static bool IsOdd(this int n) => n % 2 != 0;

    public static bool IsEachElementInExactlyOnePair(this int[] elements, IEnumerable<(int First, int Second)> pairs)
    {
        var elementCount = new Dictionary<int, int>();

        // Count occurrences of elements in pairs
        foreach (var (First, Second) in pairs)
        {
            elementCount[First] = elementCount.GetValueOrDefault(First) + 1;
            elementCount[Second] = elementCount.GetValueOrDefault(Second) + 1;
        }

        // Check if each element appears exactly once
        return elements.All(e => elementCount.GetValueOrDefault(e) == 1)
               && elements.All(e => elementCount.ContainsKey(e));
    }
}

On a Main.cs file,

using HereIGo;

static bool OddPairs(int[] array)
    => array.Pairs()
        .Where(p => p.Sum().IsOdd())
        .Combinations(array.Length / 2)
        .Any(combination => array.AllExactlyInOnePair(combination));

OddPairs([1, 2, 3, 4]); // true
OddPairs([1, 3, 5, 7]); // false
OddPairs([1, -1]); // false
OddPairs([2, -1]); // true

AI has changed the the tech interview landscape for good. The other day, I asked AI to solve another interview exercise. And I was surprised. And more recently, I’ve used Copilot to help me with these 4 boring coding tasks. Yes, AI isn’t going anywhere. So we as coders have to adapt, like we have always done.

The Most Challenging Managers to Work With (And What to Do About It)

I’ve changed my mind about what a good manager is.

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.

I worked with all kinds of managers. From those who still coded to others who hadn’t touched code in years.

The most challenging ones? The newly promoted team members.

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, they aren’t the best coders anymore.

In those situations, the best thing to do is follow the advice from Why Engineers Hate Their Managers:

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.

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

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.