Hey folks!

Shame on me, I've been working damn hard last month, as well as doing hundreds of other things in between. Even now I'm walking from work to the gym, so sorry for not spamming your feed with numerous articles. I will be a better spammer eventually.

Today is part 2 of stupid mistakes made not by us programmers. They are fundamental, by design, and this time it's not just bad naming. It's about rituals we're forced to do — or get punished if we refuse to.

Enabling "normal mode" in bash

By default, Bash works as a non-stoppable machine:

  • Current command failed? Fuck it, go on to the next one.
  • Current command produced an error? Fuck it, feed the garbage to the next command via pipe.
  • Some variable is undefined? Fuck it, let's assume it's empty.

C'mon, the last thing you want your program to do is fail at some point, generate garbage, and continue working with that garbage. But that's what bash does by default!

The ritual: every bash script must start with set -euo pipefail.

  • -e makes bash stop if some command fails;
  • -u forbids using unset variables;
  • -o pipefail — pipeline fails if any command within it fails.

If you ever forget this ritual, shit will hit the fan, severely. For example:

Dumb substitution

The root cause of many mistakes is "dumb substitution". By that term I mean exactly what it sounds like: putting one string into a placeholder as-is. It backfires. Here are some cases.

SQL injections

SQL injections are a direct result of putting values straight into the query.

SELECT * FROM users WHERE id={USER_INPUT};

Substitute USER_INPUT with 123; ANY COMMAND and run whatever you want.

The ritual: use ORM or parameterized queries, otherwise you're doomed.

C macros

Dumb simple macros:

#define SQR(x) x * x
int b = SQR(1 + 2);

This expands to:

int b = 1 + 2 * 1 + 2;

And suddenly your macro calculates God knows what.

The ritual: use parentheses around your macro, otherwise you'll have a lot of fun debugging random values.

Secret key in sources

I love Django for many things, and one of them is that when you run a startproject command, it creates a new project and puts SECRET_KEY directly into your code. Like, you spend your entire career trying not to fuck things up, not to leak secrets, to isolate them; GitHub invents action secrets, AWS invents secret variables; but Django just says "screw it all" and puts your secret right into the code, forever. Thanks, mate!

> django-admin startproject myproject
> cat myproject/settings.py | grep -i secret
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-m&!$#k(56asdf_example_random_string_89123'

Thanks for starting it with django-insecure. You could also put delete-this-secret inside the key, everyone will definitely see it, right?

The ritual: create a project, replace SECRET_KEY = os.environ("DJANGO_SECRET_KEY"), otherwise your key is baked into your code.

Creating async tasks in Python

When you create an async task in Python with asyncio.create_task(), you probably expect the task to… run. Bold assumption. But Python has other ideas. If you don't keep a reference to the created task, it may get wiped by the garbage collector at any moment. "No reference in code" = "not needed". That's how computers think, not humans. Humans think that if a task was created, it should execute (feels like I've said that already).

The ritual: save the task somewhere. Happy debugging if you forget.

tasks = []
# ...
def func():
    task = asyncio.create_task(do_stuff())
    tasks.append(task)  # <-- this

The more you go through an engineer's career, the more rituals you collect. The more rituals you collect, the more likely it is that you forget one of them. It's not our fault that we forget things, it's the fault of those who keep adding to that enormous ritual list.

None

If you want to check how many rituals I remember — and, more interestingly, how many I forget — hire me. Or sign a contract with SlashHash.dev if you're into B2B stuff.