MoreRSS

site iconMatthias EndlerModify

I’m the founder of corrode, a Rust consulting company. Before that, I was a backend engineer at trivago.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of Matthias Endler

How To Review Code

2025-08-06 08:00:00

I’ve been reviewing other people’s code for a while now, more than two decades to be precise. Nowadays, I spend around 50-70% of my time reviewing code in some form or another. It’s what I get paid to do, alongside systems design.

Over time, I learned a thing or two about how to review code effectively. I focus on different things now than when I started.

Think About The Big Picture

Bad reviews are narrow in scope. They focus on syntax, style, and minor issues instead of maintainability and extensibility.

Good reviews look at not only the changes, but also what problems the changes solve, what future issues might arise, and how a change fits into the overall design of the system.

I like to look at the lines that weren’t changed. They often tell the true story.

For example, often people forget to update a related section of the codebase or the docs. This can lead to bugs, confusion, breaking changes, or security issues.

Be thorough and look at all call-sites of the new code. Have they been correctly updated? Are the tests still testing the right thing? Are the changes in the right place?

Here’s a cheat sheet of questions I ask myself when reviewing code:

  • How does this code fit into the rest of the system?
  • What’s its interaction with other parts of the codebase?
  • How does it affect the overall architecture?
  • Does it impact future planned work?

These questions have more to do with systems design than with the changes themselves. Don’t neglect the bigger picture because systems become brittle if you accept bad changes.

Code isn’t written in isolation. The role of more experienced developers is to reduce operational friction and handle risk management for the project. The documentation, the tests, and the data types are equally as important as the code itself.

Always keep an eye out for better abstractions as the code evolves.

Naming Is Everything

I spend a big chunk of my time thinking about good names when reviewing code.

Naming things is hard, which is why it’s so important to get it right. Often, it’s the most important part of a code review.

It’s also the most subjective part, which makes it tedious because it’s hard to distinguish between nitpicking and important naming decisions.

Names encapsulate concepts and serve as “building blocks” in your code. Bad names are the code smell that hint at problems running deep. They increase cognitive overhead by one or more orders of magnitude.

For example, say we have a struct that represents a player’s stats in a game:

struct Player {
    username: String,
    score: i32,
    level: i32,
}

I often see code like this:

// Bad: using temporary/arbitrary names creates confusion
fn update_player_stats(player: Player, bonus_points: i32, level_up: bool) -> Player {
    let usr = player.username.trim().to_lowercase();
    let updated_score = player.score + bonus_points;
    let l = if level_up { player.level + 1 } else { player.level };
    let l2 = if l > 100 { 100 } else { l };
    
    Player {
        username: usr,
        score: updated_score, 
        level: l2,
    }
}

This code is hard to read and understand. What is usr, updated_score, and l2? The purpose is not conveyed clearly. This builds up cognitive load and make it harder to follow the logic.

That’s why I always think of the most fitting names for variables, even if it feels like I’m being pedantic.

// Good: meaningful names that describe the transformation at each step
fn update_player_stats(player: Player, bonus_points: i32, level_up: bool) -> Player {
    // Each variable name describes what the value represents
    let username = player.username.trim().to_lowercase();
    let score = player.score + bonus_points;

    // Use shadowed variables to clarify intent
    let level = if level_up { player.level + 1 } else { player.level };
    let level = if level > 100 { 100 } else { level };
    
    // If done correctly, the final variable names
    // often match the struct's field names
    Player {
        username,
        score,
        level,
    }
}

Good names become even more critical in larger codebases where values are declared far away from where they’re used and where many developers have to have a shared understanding of the problem domain.

Don’t Be Afraid To Say “No”

I have to decline changes all the time and it’s never easy. After all someone put in a lot of effort and they want to see their work accepted.

Avoid sugarcoating your decision or trying to be nice. Be objective, explain your reasoning and provide better alternatives. Don’t dwell on it, but focus on the next steps.

It’s better to say no than to accept something that isn’t right and will cause problems down the road. In the future it will get even harder to deny a change once you’ve set a precedent.

That’s the purpose of the review process: there is no guarantee that the code will be accepted.

In open source, many people will contribute code that doesn’t meet your standards. There needs to be someone who says “no” and this is a very unpopular job (ask any open source maintainer). However, great projects need gatekeepers because the alternative is subpar code and eventually unmaintainable projects.

At times, people will say “let’s just merge this and fix it later.” I think that’s a slippery slope. It can lead to tech debt and additional work later on. It’s hard to stand your ground, but it’s important to do so. If you see something that isn’t right, speak up.

When it gets hard, remember that you’re not rejecting the person, you’re rejecting the code. Remind people that you appreciate their effort and that you want to help them improve.

Even though you’ll develop an intuition for what to focus on in reviews, you should still back it up with facts. If you find yourself saying “no” to the same thing over and over again, consider writing a style guide or a set of guidelines for your team.

Be gracious but decisive; it’s just code.

Code Review Is Communication

Code reviews aren’t just about code; people matter too. Building a good relationship with your coworkers is important.

I make it a point to do the first couple of reviews together in a pair programming session if possible.

This way, you can learn from each other’s communication style. Building trust and getting to know each other works well this way. You should repeat that process later if you notice a communication breakdown or misunderstanding.

Use Multiple Iterations Of Reviews

“Can you take a quick look at this PR? I want to merge it today.” There often is an expectation that code reviews are a one-time thing. That’s not how it works. Instead, code reviews are an iterative process. Multiple iterations should be expected to get the code right.

In my first iteration, I focus on the big picture and the overall design. Once I’m done with that, I go into the details.

The goal shouldn’t be to merge as quickly as possible, but to accept code that is of high quality. Otherwise, what’s the point of a code review in the first place? That’s a mindset shift that’s important to make.

Reviews aren’t exclusively about pointing out flaws, they’re also about creating a shared understanding of the code within the team. I often learn the most about writing better code by reviewing other people’s code. I’ve also gotten excellent feedback on my own code from excellent engineers.

These are invaluable “aha moments” that help you grow as a developer. Experts spent their valuable time reviewing my code, and I learned a lot from it. I think everybody should experience that once in their career.

Don’t Be A Jerk

From time to time, you’ll disagree with the author. Being respectful and constructive is important. Avoid personal attacks or condescending language. Don’t say “this is wrong.” Instead, say “I would do it this way.” If people are hesitant, ask a few questions to understand their reasoning.

  • “Will this break existing workflows if we do it this way?”
  • “Which alternatives have you considered?”
  • “What happens if you call this function with an empty array?”
  • “If I don’t set this value, what will be the error message presented to the user?”

These “Socratic questions”1 help the author think about their decisions and can lead to better designs.

People should enjoy receiving your feedback. If not, revisit your review style. Only add comments that you yourself would be happy to receive.

From time to time, I like to add positive comments like “I like this” or “this is a great idea.” Keeping the author motivated and showing that you appreciate their work goes a long way.

If Possible, Try To Run The Code

It’s easy to miss subtle details when you look at code for too long. Having a local copy of the code that I can play with helps me a lot.

I try to run the code, the tests, and the linters if I can. Checking out the branch, moving things around, breaking things, and trying to understand how it works is part of my review process.

User-facing changes like UI changes or error messages are often easier to spot when you run the code and try to break it.

After that, I revert the changes and, if needed, write down my findings in a comment. Better understanding can come from this approach.

Be Upfront About Your Availability

Code reviews are often a bottleneck in the development process, because they can’t be fully automated: there’s a human in the loop who has to look at the code and provide feedback.

But if you wait for your colleagues to review your code, that can lead to frustration. Avoid being that person.

Sometimes you won’t have time to review code and that is okay. If you can’t review the code in a reasonable time, let the author know.

I’m still working on this, but I try to be more proactive about my availability and set clear expectations.

Never Stop Learning

Code reviews are my favorite way to learn new things. I learn new techniques, patterns, new libraries, but most importantly, how other people approach problems.

I try to learn one new thing with each review. It’s not wasted time, if it helps the team improve and grow as a whole.

Don’t Be Nitpicky

Formatters exist for a reason: leave whitespace and formatting to the tools. Save your energy for issues that truly matter.

Focus on logic, design, maintainability, and correctness. Avoid subjective preferences that don’t impact code quality.

Ask yourself: Does this affect functionality or would it confuse future developers? If not, let it go.

Focus On The Why, Not The How

When reviewing code, focus on the reasoning behind the changes. This has a much better chance of success than pointing out flaws without any reasoning.

Consider the following two code review comments. The first one is unhelpful and dismissive.

A code review comment just saying: “Don’t do this.”

The second suggests an alternative, links to the documentation, and explains why the change could lead to problems down the road.

A code review comment explaining the reasoning behind rejecting the change by providing a helpful alternative and a link to the docs

Which one would you prefer to receive?

I realize that this requires more time and effort, but it’s worth it! Most of the time, the author will appreciate it and avoid making the same mistake in the future. There is a compound effect from helpful reviews over time.

Don’t Be Afraid To Ask Stupid Questions

Asking is better than assuming. If you don’t understand something, ask the author to explain it. Chances are, you’re not the only one who doesn’t get it.

Often, the author will be happy to explain their reasoning. Better understanding of the code and the system as a whole can result from this. It can also help the author see things from a different perspective. Perhaps they’ll learn that their assumptions were wrong or that the system isn’t self-explanatory. Perhaps there’s missing documentation?

Asking great questions is a superpower.

Ask For Feedback On Your Reviewing Style

From time to time, ask the author for feedback on your feedback:

  • Have you been too harsh/nitpicky/slow/sloppy?
  • Did you point out the right things?
  • Did your feedback help them?
  • Do they have suggestions for improvement?

Basically, you ask them to review your review process, heh.

Learning how to review code is a skill that needs constant practice and refinement. Good luck finding your own style.

  1. Thanks for pointing out that term to me, Lucca!

Repeat Yourself

2025-06-23 08:00:00

One of the most repeated pieces of advice throughout my career in software has been “don’t repeat yourself,” also known as the DRY principle. For the longest time, I took that at face value, never questioning its validity.

That was until I saw actual experts write code: they copy code all the time1. I realized that repeating yourself has a few great benefits.

Why People Love DRY

The common wisdom is that if you repeat yourself, you have to fix the same bug in multiple places, but if you have a shared abstraction, you only have to fix it once.

Another reason why we avoid repetition is that it makes us feel clever. “Look, I know all of these smart ways to avoid repetition! I know how to use interfaces, generics, higher-order functions, and inheritance!”

Both reasons are misguided. There are many benefits of repeating yourself that might get us closer to our goals in the long run.

Keeping Up The Momentum

When you’re writing code, you want to keep the momentum going to get into a flow state. If you constantly pause to design the perfect abstraction, it’s easy to lose momentum.

Instead, if you allow yourself to copy-paste code, you keep your train of thought going and work on the problem at hand. You don’t introduce another problem of trying to find the right abstraction at the same time.

It’s often easier to copy existing code and modify it until it becomes too much of a burden, at which point you can go and refactor it.

I would argue that “writing mode” and “refactoring mode” are two different modes of programming. During writing mode, you want to focus on getting the idea down and stop your inner critic, which keeps telling you that your code sucks. During refactoring mode, you take the opposite role: that of the critic. You look for ways to improve the code by finding the right abstractions, removing duplication, and improving readability.

Keep these two modes separate. Don’t try to do both at the same time.2

Finding The Right Abstraction Is Hard

When you start to write code, you don’t know the right abstraction just yet. But if you copy code, the right abstraction reveals itself; it’s too tedious to copy the same code over and over again, at which point you start to look for ways to abstract it away. For me, this typically happens after the first copy of the same code, but I try to resist the urge until the 2nd or 3rd copy.

If you start too early, you might end up with a bad abstraction that doesn’t fit the problem. You know it’s wrong because it feels clunky. Some typical symptoms include:

  • Generic names that don’t convey intent, e.g., render_pdf_file instead of generate_invoice
  • Difficult to understand without additional context
  • The abstraction is only used in one or two places
  • Tight coupling to implementation details

It’s Hard To Get Rid Of Wrong Abstractions

We easily settle for the first abstraction that comes to mind, but most often, it’s not the right one. And removing the wrong abstraction is hard work, because now the data flow depends on it.

We also tend to fall in love with our own abstractions because they took time and effort to create. This makes us reluctant to discard them even when they no longer fit the problem—it’s a sunk cost fallacy.

It gets worse when other programmers start to depend on it, too. Then you have to be careful about changing it, because it might break other parts of the codebase. Once you introduce an abstraction, you have to work with it for a long time, sometimes forever.

If you had a copy of the code instead, you could just change it in one place without worrying about breaking anything else.

Duplication is far cheaper than the wrong abstraction

—Sandi Metz, The Wrong Abstraction

Better to wait until the last moment to settle on the abstraction, when you have a solid understanding of the problem space.3

The Mental Overhead of Abstractions

Abstraction reduces code duplication, but it comes at a cost.

Abstractions can make code harder to read, understand, and maintain because you have to jump between multiple levels of indirection to understand what the code does. The abstraction might live in different files, modules, or libraries.

The cost of traversing these layers is high. An expert programmer might be able to keep a few levels of abstraction in their head, but we all have a limited context window (which depends on familiarity with the codebase).

When you copy code, you can keep all the logic in one place. You can just read the whole thing and understand what it does.

Resist The Urge Of Premature Abstraction

Sometimes, code looks similar but serves different purposes.

For example, consider two pieces of code that calculate a sum by iterating over a collection of items.

total = 0
for item in shopping_cart:
    total += item.price * item.quantity

And elsewhere in the code, we have

total = 0
for item in package_items:
    total += item.weight * item.rate

In both cases, we iterate over a collection and calculate a total. You might be tempted to introduce a helper function, but the two calculations are very different.

After a few iterations, these two pieces of code might evolve in different directions:

def calculate_total_price(shopping_cart):
    if not shopping_cart:
        raise ValueError("Shopping cart cannot be empty")
    
    total = 0.0
    for item in shopping_cart:
        # Round for financial precision
        total += round(item.price * item.quantity, 2)
    
    return total

In contrast, the shipping cost calculation might look like this:

def calculate_shipping_cost(package_items, destination_zone):
    # Use higher of actual weight vs dimensional weight
    total_weight = sum(item.weight for item in package_items)
    total_volume = sum(item.length * item.width * item.height for item in package_items)
    dimensional_weight = total_volume / 5000  # FedEx formula
    
    billable_weight = max(total_weight, dimensional_weight)
    return billable_weight * shipping_rates[destination_zone]

Had we applied “don’t repeat yourself” too early, we would have lost the context and specific requirements of each calculation.

DRY Can Introduce Complexity

The DRY principle is misinterpreted as a blanket rule to avoid any duplication at all costs, which can lead to complexity.

When you try to avoid repetition by introducing abstractions, you have to deal with all the edge cases in a place far away from the actual business logic. You end up adding redundant checks and conditions to the abstraction, just to make sure it works in all cases. Later on, you might forget the reasoning behind those checks, but you keep them around “just in case” because you don’t want to break any callers. The result is dead code that adds complexity to the codebase; all because you wanted to avoid repeating yourself.

The common wisdom is that if you repeat yourself, you have to fix the same bug in multiple places. But the assumption is that the bug exists in all copies. In reality, each copy might have evolved in different ways, and the bug might only exist in one of them.

When you create a shared abstraction, a bug in that abstraction breaks every caller, breaking multiple features at once. With duplicated code, a bug is isolated to just one specific use case.

Clean Up Afterwards

Knowing that you didn’t break anything in a shared abstraction is much harder than checking a single copy of the code. Of course, if you have a lot of copies, there is a risk of forgetting to fix all of them.

The key to making this work is to clean up afterwards. This can happen before you commit the code or during a code review.

At this stage, you can look at the code you copied and see if it makes sense to keep it as is or if you can see the right abstraction. I try to refactor code once I have a better understanding of the problem, but not earlier.

A trick to undo a bad abstraction is to inline the code back into the places where it was used. For a while, you end up “repeating yourself” again in the codebase, but that’s okay. Rethink the problem based on the new information you have. Often you’ll find a better abstraction that fits the problem better.

When the abstraction is wrong, the fastest way forward is back.

—Sandi Metz, The Wrong Abstraction

tl;dr

It’s fine to look for the right abstraction, but don’t obsess over it. Don’t be afraid to copy code when it helps you keep momentum and find the right abstraction.

It bears repeating: “Repeat yourself.”

  1. For some examples, see Ferris working on Rustendo64 or tokiospliff working on a C++ game engine.

  2. This is also how I write prose: I first write a draft and block my inner critic, and then I play the role of the editor/critic and “refactor” the text. This way, I get the best of both worlds: a quick feedback loop which doesn’t block my creativity, and a final product which is more polished and well-structured. Of course, I did not invent this approach. I recommend reading “Shitty first drafts” from Anne Lamott’s book Bird by Bird: Instructions on Writing and Life if you want to learn more about this technique.

  3. This is similar to the OODA loop concept, which stands for “Observe, Orient, Decide, Act.” It was developed by military strategist John Boyd. Fighter pilots use it to wait until the last responsible moment to decide on a course of action, which allows them to make the best decision based on the current situation and available information.

Watching Millionaires

2025-06-06 08:00:00

I watched the Champions League final the other day when it struck me: I’m basically watching millionaires all the time.

The players are millionaires, the coaches are millionaires, the club owners are millionaires. It’s surreal.

This week I watched John Wick Ballerina and, again, there’s Keanu Reeves, who is a millionaire, and Ana de Armas, who is as well.

Yesterday I heard about Trump and Musk fighting. They are not millionaires, they are billionaires!

As I’m writing this, I’m watching the Rock am Ring live stream, a music festival in Germany. Weezer is playing. These guys are all millionaires.

I don’t know what to make of it. It’s a strange realization, but one that feels worth sharing.

I could go down the road of how this fixation on elites distracts us from the people nearby, but that’s not quite it. What interests me more is how normalized this has become.

Maybe it’s just the power law in action: a few rise to the top, and we amplify them by watching. But most people in every field aren’t millionaires. We just don’t see them.

You’re on a tiny blog by a tiny man and if you made it this far, I appreciate you. It looks as if you care about the little stories as well.

If you’re anything like me, you’re not only enjoying the little stories, you’re actively seeking them out – but there’s so few of it nowadays. Yes, there are still places where people share their stories, but you need to know where to look.

If anything, we all should share more. Write about the little things, the everyday moments, the people you meet, the things you care about. Don’t live anybody else’s life!

Rivers Cuomo, Weezer’s lead singer, once wrote:

My motivation is much different now than it was then: then I was terribly discontent and dreaming of being a classical composer, a writer, or basically anything that I wasn’t; now I just want to enjoy my life and do the responsible thing—graduate.

That’s from his Letter For Readmission To Harvard (2005).

Nobody forced him to go back to Harvard after so many years. He was a freaking millionaire rock star by then.

And yet, he did.

He stopped pretending and started living.

We don’t have to keep watching other people’s lives.

Live your own.

Paolo the Plumber

2025-06-02 08:00:00

Paolo was a plumber.

People knew him as a reliable and thorough craftsman. He fixed the pipes in his small town and made a good living doing so.

One day, his friend Mario told him that he’d bought a plumbing machine. Paolo was intrigued and asked how it worked.

“It’s magical!” said Mario. “I show it what’s broken, and it fixes the problem in no time!” Paolo asked if he could watch the machine work.

The next day, Paolo and Mario took the machine to a house with a broken pipe. Paolo watched as Mario positioned the machine by the pipe. “Beep boop,” and the machine started working, and quickly. Paolo noticed the machine turned the wrench back and forth instead of steady pressure - something he could adapt for his own work. Within minutes, the pipe was fixed.

“Soon no one will need plumbers anymore,” said Mario. “I can already do the work of ten plumbers with this machine!”

That night, Paolo couldn’t sleep. He thought about his job and how it might change. He loved being a plumber and helping people. But what if machines really took over?

Within a few weeks, Paolo’s phone stopped ringing. People were calling Mario instead because he did quicker, cheaper work. Some of Paolo’s old customers told him he was “old-fashioned” and “out of touch.”

In the past, none of his customers had ever complained about his work. He always took time to do things right. He would check every joint, seal every pipe, and make sure everything was perfect before leaving. Sometimes he noticed other problems that needed fixing and he would offer to fix those too.

Then one day, he got a call from an old customer. It was an emergency. The pipes in the restaurant were leaking and they needed help fast. Paolo rushed over and found a mess. He got to work and fixed the problem.

“We just got it fixed the other day!” When Paolo asked who did the work, the owner said it was Mario.

From that day on, more people called Paolo. They all had problems after working with Mario and the machine. Paolo kept finding the same mistakes: pipes not properly sealed, joints not aligned correctly, leaks temporarily fixed with instant glue. Sometimes the machine would add extra parts: pipes that ended nowhere, valves that didn’t connect to anything. Paolo recognized these as signs of the machine at work.

Paolo called Mario and told him what he’d found. Mario knew about the issues: “I told it to fix it, but it didn’t work right. Even when I asked multiple times and was very polite.” And worse: “One time I looked away for a moment and the machine started remodeling the bathroom! It added a new sink that wasn’t there before.”

Paolo asked why he didn’t just fix it himself. “I can’t,” Mario said. “I don’t know how to do it without the machine.”

Mario had been a reputable plumber before he got the machine. Now he was relying on a machine that didn’t always work. Worse, Mario didn’t own the machine but rented it from a company far away. The rent was cheap in the beginning, but now it was getting more expensive.

Paolo realized that Mario wasn’t the only one. Many plumbers were using machines now, and new plumbers were learning machines instead of tools. It wasn’t just plumbers—electricians, carpenters, other tradespeople were all relying on machines. The machines caused problems, but the company promised they would fix everything and get better with time. They kept updating the machines and gave them fancy names, but the problems remained.

Paolo just kept working. He fixed what the machines broke. His customers called him back for more work. Soon his phone was ringing like before.

A while later, a salesperson came to town with a new machine. Paolo heard Mario talking to him at the coffee shop.

Reinvent the Wheel

2025-05-24 08:00:00

One of the most harmful pieces of advice is to not reinvent the wheel.

It usually comes from a good place, but is typically given by two groups of people:

  • those who tried to invent a wheel themselves and know how hard it is
  • those who never tried to invent a wheel and blindly follow the advice

Either way, both positions lead to a climate where curiosity and exploration gets discouraged. I’m glad that some people didn’t follow that advice; we owe them many of the conveniences of modern life.

Even on a surface level, the advice is bad: We have much better wheels today than 4500–3300 BCE when the first wheel was invented. It was also crucially important that wheels got reinvented throughout civilizations and cultures.

Note: When I say “wheel” throughout this post, please replace it with whatever tool, protocol, service, technology, or other invention you’re personally interested in.

Inventing Wheels Is Learning

“What I cannot create, I do not understand”
Richard Feynman, Physicist and Nobel Prize Winner

To really understand something on a fundamental level, you have to be able to implement a toy version first. It doesn’t matter if it’s any good; you can throw it away later.

In Computer Science, for example, there are many concepts that are commonly assumed to be beyond the abilities of mere mortals: protocols, cryptography, and web servers come to mind.

More people should know how these things work. And therefore I think people should not be afraid to recreate them.

Everything Is A Rabbit Hole

Too often, fundamental things are taken for granted. For example strings or paths are super complicated concepts in programming. It’s a great exercise to implement a string or a path library yourself if you’re interested in how they work.

Even if nobody ends up using your work, I bet you’ll learn a lot. For example:

  • There is an infinite complexity in everyday things.
  • Building something that even a single other person finds useful is a humbling experience.
  • Humans like you created these abstractions. They are not perfect and you can make different tradeoffs in your own design.

On the last point, everything is a tradeoff and there are dozens, sometimes hundreds of footguns with every toy problem.

Along the way, you will have to make decisions about correctness, simplicity, functionality, scalability, performance, resource usage, portability, and so on.

Your solution can be great in some of these things, but not all of them and not for all users. That also implies that existing solutions have flaws and might not be designed to solve your particular problem; no matter how well-established the solution is.

Going down rabbit holes is fun in its own way, but there is one other benefit: It is one of the few ways to level up as an engineer… but only if you don’t give up before you end up with a working version of what you tried to explore. If you jump between projects too often, you will learn nothing.

Reasons for Reinventing the Wheel

There are great reasons to reinvent the wheel:

  • Build a better wheel (for some definition of better)
  • Learn how wheels are made
  • Teach others about wheels
  • Learn about the inventors of wheels
  • Be able to change wheels or fix them when they break
  • Learn the tools needed to make wheels along the way
  • Learn a tiny slice of what it means to build a larger system (such as a vehicle)
  • Help someone in need of a very special wheel. Maybe for a wheelchair?

Who knows? The wheel you come up with might not be the best use for a car, but maybe for a… skateboard or a bike? Or you fail building a nicer wheel, but you come up with a better way to test wheels along the way. Heck, your wheel might not even be meant for transportation at all! It might be a potter’s wheel, “a machine used in the shaping (known as throwing) of clay into round ceramic ware” according to Wikipedia. You might end up building a totally different kind of wheel like a steering wheel or a flywheel. We need more people who think outside the box.

Reuse vs Reinvent

Of course, don’t disregard the works of others – study their work and reuse where you see fit. Don’t reinvent the wheel out of distrust or ignorance of the work of others. On the other side, if you never tried to put your knowledge to the test, how would you ever learn enough about your field to advance it?

I observed you can move very quickly by running little experiments. Especially in software engineering, building small prototypes is cheap and quick. Solve your own problem, start small, keep it simple, iterate.

So, with all of the above, here’s my advice:

Reinvent for insight. Reuse for impact.

No Matter What

2025-04-13 08:00:00

As kids, our parents established a few simple rules that we would all follow, no matter the circumstances. One of them was that we’d always have dinner together in the evening, typically around 6pm.

In almost two decades, they never broke that rule. We had dinner on 9/11 and when mom was at the hospital. It’s not always easy.

There’s a nice thing that happens when you have such a golden rule: it has ripple effects. Since we had dinner together every evening, we would always have time to talk about the day. Problems would be uncovered earlier. We would know about each other’s appointments for the next day. It provided structure throughout the rest of the day. It put things into perspective. It grounded us.

  • Bad grade at school? Dinner at 6.
  • Played computer games all afternoon and lost track of time? Dinner at 6.
  • No matter how bad your day was, dinner is always waiting for you.

As a kid, it sounded like one of those “stupid” rules only grown-ups would come up with. And in fact, my parents knew that it was stupid. They did it anyway. As a kid, that made their life look extremely dull and boring. I remember pitying my dad once for being such a slave to society. Yet, they persisted because without it, things would fall apart. Skipping dinner is about way more than skipping dinner.

These Rules Are Simple, But Not Easy

It’s a simple rule with little room for interpretation. However, it’s not easy: there are times when you have to drop something else to make dinner at 6 work. That’s when the rule counts the most! That’s what makes or breaks it.

Following the rule 90% of the time is much easier than following it 100% of the time. You have to make sacrifices. You have to say no sometimes. That’s the price it takes to stick to the rule.

Yes, such rules “sound” stupid, but there’s a deeper, almost stoic realization to it: Life is complicated and will throw obstacles in your way. But if you really want to make progress, you have to find a way. If nothing else helps, make up a stupid rule; and the harder you struggle, the more specific the rule should be.

Dinner. Every day. At 6 o’clock.

Only now am I discovering this for myself. In 2019, I mentioned to my friend Abu that I felt bad for not doing any sports. It’s not that I didn’t try, it’s just that nothing lasted for long. He suggested going for a run together on Tuesdays – no matter what. I thought that was ridiculous. I told him that it couldn’t possibly work. Why Tuesdays of all days!? It felt so random. In my mind, I started negotiating. But there’s no point in negotiating with irrationality. Fast forward 5 years, and I still run every Tuesday.

I actually suck at running. My pace isn’t fast. The distance isn’t far, but it’s a solid effort. Time was made. It worked out. Again, it had positive rippling effects: I ran on Crete in Greece and Sardinia in Italy. Different people joined me on my runs. If Tuesday finds me elsewhere, my running shoes come along. Now, did I always manage to run on a Tuesday? No. It’s not easy! But I always gave it a solid attempt and I can remember each time I didn’t run. Since Abu and I run together a lot, we would talk about our week. If we didn’t make up that rule, we would never have started to know each other on such a deep level.

Some people won’t understand when you tell them that you have to do a thing “no matter what.” Instead of telling them I have to go for a run, I say I’m busy that evening. Nobody ever asks any questions.

Isn’t this just a habit?

With “no matter what” there can be serious consequences. If you have to take care of a loved one, you can’t skip a day. Or if you’re an Air Traffic Controller, failure is not an option.

My stakes are not as high, but I take them very seriously.

“No matter what” rules aren’t habits, at least not in the beginning. They can, however, turn into super strong habits with time.

I found that the best way to implement a “NMW Rule” is to do it on the spot. When my dentist asked me if I floss every day (I didn’t), I made the decision to start right then and there and never skip a day.

Another good way to get started is to take on some lightweight responsibility. For example, I recommend getting plants. Then you have to water them – no matter what.

If the plant dries out, you broke the rule; simple as that. The great thing is that the watering interval is usually pretty low, so there’s time to get used to it (but getting used to it you must).

If it works, you’ll enjoy the feeling of continuity. It’s like a chain of good deeds. A new habit is born.

In the past, I never had any plants. Now our apartment is full of them. I love the companionship and the continuity.

What’s your “NMW”?

If you already have a “no matter what” rule, you have my deepest respect.

If not, whether you want to write that book, run that marathon, or just save a few bucks each month, make it work – no matter what.