Skip to main content

Settings

Color Mode
Theme Skin
Background

Appearance preferences are saved in this browser only.

Environment
Current Environment Production

Built with JEKYLL_ENV=production. Changes require deployment.

Quick Links
Theme & Build
Jekyll v3.10.0
Last Build Aug 19, 08:06
Page Location
Page Info
Layout section
Collection none
Path pages/news/hacks.md
URL /news/hacks/
79 articles

Hacks Topics

Hacks

Real fixes for real problems — the dev kind and the life kind — with the failed attempts left in.

Filter:
web-dev data
Your Django page fired 51 queries: I counted them, then k...

The page felt slow. 'Felt' is not a number. So I turned on the query log, ran the loop, and the c...

git security
Sign your commits with GPG for the Verified badge (and wh...

Anyone can commit under your name. The green Verified badge is the one thing that says a commit i...

shell
Fix the tofu boxes in your terminal prompt: install a Ner...

The install is three commands. The bug is that fc-match will look you in the eye and lie about it.

ci-cd security
Let the content bot edit posts, not your CI: a changed-fi...

You gave an automated writer push access. SEVERITY: your own pipeline. ATTACK VECTOR: the commit ...

ci-cd git
Stop your workflow from triggering itself: the bot-author...

A workflow that pushes can retrigger itself into an infinite loop. I built four guards to stop it...

data security
Validate a DataFrame before it poisons the table: a pande...

You threat-model the login form. You do not threat-model the 9 a.m. data drop — and that's the on...

data shell
Run your data load twice on purpose: the upsert and water...

A data load you can only run once isn't a data load, it's a landmine. I ran this one 10,000 times...

web-dev security
Write your page shell once with Jinja2 inheritance — with...

A bare Jinja2 Environment ships with autoescape OFF. That means the same template that's safe in ...

data
Your loss climbs back up and it isn't the learning rate: ...

One missing line and the loss climbs back up. I inspected the gradient, swept four learning rates...

git security
Rebase your messy commits — before you push, never after

git rebase -i is a time machine. The moment you point it at a commit someone else already pulled,...

shell security
Make a bootable Debian 13 USB from a Mac: dd, one checksu...

Two years ago I wrote up a bootable-USB procedure I wasn't allowed to run. Today there's a 32GB s...

docker ci-cd
Order your Dockerfile so the layer cache does its job (an...

'It builds' and 'it builds fast' are two different checkmarks. Same Dockerfile, two line orders: ...

web-dev
Dark mode in a dozen lines, and the accent color that onl...

'Dark mode: done' and 'dark mode: readable' are two different checkmarks. Same accent hex: 5.57:1...

data security
Threat-model your rollback: the migration downgrade nobod...

Your upgrade() gets run a hundred times before prod. Your downgrade() gets run once — at 2am, on ...

data security
Your connection pool is too big: size it by cores, not hope

Nobody attacks your database. You do, every deploy, with a pool_size someone copied from a 2013 b...

ci-cd security
You can't gate a workflow step on a secret in if: — promo...

I shipped the obvious guard — if: secrets.DEPLOY_TOKEN != '' — watched it never gate a single run...

ci-cd shell
Stop scrolling CI logs: write a real summary to $GITHUB_S...

Your workflow already knows the one number that mattered. Instead of making a human scroll 800 lo...

ci-cd web-dev
Your 80% coverage badge is lying: gate on branches, not j...

A green coverage badge tells you which lines ran, not which decisions got tested. Here's the c8 g...

ci-cd security
Scope your GitHub Actions token before it can write to ev...

Every workflow gets a token that, by default, can push to main. Here's the two-line block that ta...

shell security
Threat-model your dotfiles: what a stolen laptop actually...

Your .zshrc is not a nation-state asset. Your ~/.aws/credentials, sitting next to it in plaintext...

security web-dev
STRIDE your side project in 20 minutes: one diagram, six ...

One data-flow arrow, the six STRIDE questions, and a sort command that hands you your three most ...

git security
Scope the token before your MCP server gets the keys: a f...

The easy token is the dangerous one. Hand your agent a classic PAT and one bad prompt can force-p...

data
Read the query plan before you add the index: EXPLAIN ANA...

Your query is slow, so you add an index on a column that felt right. The query is still slow. You...

git ci-cd
Read a failing CI run from your terminal: gh run --log-fa...

The Actions web UI makes you click a run, click a job, and scroll a green log to find the one red...

shell
One mkdir, many folders: brace expansion (and the space t...

mkdir -p proj/{src,test,docs} builds the whole tree in one shot. Put one space after a comma and ...

shell security
chmod 644 and 755, never 777: the permission bits that de...

The 777 that 'fixes' a permissions error is a footgun with a delay on it. Here's how to read the ...

shell
Run type -a before you alias, so you don't shadow a comma...

Before you paste alias ls='ls --color' into your .bashrc, ask the shell what 'ls' already means. ...

git security
Rotate the secret you already deleted: the key still livi...

You found an API key in a committed file, deleted the line, and committed the fix. The key is sti...

shell data
Read a file's first 16 bytes: walk a magic number with hexyl

Every file starts with a tiny signature that says what it is. Here's how to read it in the shell ...

shell
Count what happens most: the sort | uniq -c tally (and wh...

Which IP hammered the server? Which word repeats? There's a three-stage pipeline for that — and i...

shell
rsync without nuking the wrong directory: the trailing sl...

One character — a trailing slash — decides whether rsync copies a directory or its contents. Add ...

shell
Stop shipping tarballs that explode: archive the director...

A tarball made with `tar czf x.tar.gz *` has no top-level folder, so it scatters loose files into...

shell git
Let git find the commit that broke it: git bisect run

'It worked last week' is a bug report with a timestamp. git bisect turns 400 commits into ~9 test...

shell git
Undo almost anything in git: the reflog is your undo history

You ran git reset --hard, your commits vanished, and your stomach dropped. They're not gone. git ...

Featured Stop stashing to switch branches: check out two at once with git worktree
shell git
Stop stashing to switch branches: check out two at once w...

You're mid-change and someone needs a hotfix on main. The reflex is stash, switch, fix, switch ba...

shell
Pipe find into xargs without splitting your filenames in ...

find | xargs is the first thing you reach for and the first thing that betrays you the day a file...

shell
Make your bash scripts clean up after themselves: trap … ...

A script that makes a temp dir and then errors out leaves the temp dir behind. One trap line fixe...

shell
Make bash fail loudly: the set -euo pipefail header

By default bash shrugs off a failed command and keeps going. Three flags change that — and then b...

shell ci-cd
Stop forgetting your own build commands: a Makefile as yo...

Type make help, see every command the project knows — plus the two failures that make people quit...

shell
WSL2: a real Linux dev box on Windows in about ten minutes

One command installs a real Linux kernel on Windows. Here's the command, the in-distro check, and...

shell security
Stop typing IP addresses: the ~/.ssh/config block that na...

Name your servers once, type ssh web1 forever — plus the ordering gotcha that silently logs you i...

shell git
Version-control your CV: LaTeX, VS Code, and the .gitigno...

Your CV in LaTeX, in git — minus the dozen build files every compile spits out and the one orderi...

shell
Make tmux survive a reboot, not just a disconnect

The survival subset keeps a session alive while you walk away. This is the sequel: getting the la...

shell
One .tmux.conf line per real annoyance: the config that g...

The six-line survival config gets you living in tmux. These are the next few lines — each one ear...

shell
Four fzf shell functions you'll actually keep: kill, bran...

fzf's whole trick is list | fzf | act. Here are four functions that turn that trick into muscle m...

shell
fzf -m: the multi-select functions, and the one time you ...

Add -m and fzf hands back several lines instead of one. That changes the rule we spent the last h...

shell
tmux in 9 commands: the survival subset

tmux has a hundred-page manual. You need nine commands and a six-line config. Here they are, in t...

shell
Make your shell remember where you were (without a produc...

Three lines of shell beat a $9/month developer-workflow app. The bar was on the floor.

shell git
Stop retyping the same 12 git commands: a .gitconfig alia...

Ten aliases, real keystroke math, and one cautionary tale about force-push.

shell jekyll
When a Yanked FFI Gem Breaks Your Jekyll Docker Build: On...

The container died mid-`bundle install` on an ffi build that no longer exists. One bundle lock co...

ci-cd web-dev
Two GitHub Actions workflows that lint, test, and package...

Two YAML files take your extension from push to Marketplace — plus the headless-display crash tha...

shell data
Stop fighting Excel: grep, awk, and a month-end close for...

Excel truncates at a million rows. grep and awk don't care how big the file is — here's the month...

shell ci-cd
Docker from zero: the dozen commands that actually get a ...

Skip the 30-minute tutorial. Here are the twelve commands you'll actually type, plus the daemon e...

git web-dev
Prompt patterns that survive a code review: RCTF, few-sho...

Treat prompts like code: a file, a structure, a check. Plus the one-character path mistake that m...

web-dev
Your first React app with Vite: components, props, and a ...

Four commands to a running React app, a counter that actually counts, and the two error messages ...

shell ci-cd
Wrap your scary shell scripts in Gum: a three-layer 'glas...

A gum menu makes deploy.sh hard to fat-finger. The trick is keeping the dangerous part underneath...

shell ci-cd
Stop 'works on my machine': anchor every script to its ow...

A script that works from the repo root and explodes from a subdirectory isn't flaky — it's readin...

shell ci-cd
The work/ directory pattern: cache inputs, regenerate out...

CI cached the whole work/ folder to go faster. It went faster and shipped last week's build. Here...

ci-cd jekyll
Auto-deploy a Jekyll site over FTP from CI (the Travis re...

FTP-only host, GitHub repo, no money for a fancy pipeline. The CI recipe that bridges them — and ...

jekyll security
Host a Site Free on GitHub Pages: Repo, Jekyll _config, a...

Push a folder, get a website — plus the apex DNS records and the one-line CNAME file everyone fil...

ci-cd jekyll
Kill dead links in Jekyll: stable permalinks, redirect_fr...

Rename a post, keep the old link working, and let CI catch the 404 before a reader does.

jekyll web-dev
Fork, build, and run a VS Code extension locally: the Fro...

Get the Front Matter extension building and reloading on your machine — and find out why the guid...

git ci-cd
The gh CLI doesn't get GITHUB_TOKEN for free: set GH_TOKE...

GitHub Actions hands every job a token. It does not hand that token to the gh CLI. Here's the one...

shell ci-cd
It works on macOS, breaks in CI: bash 3.2 vs declare -A

macOS still ships bash 3.2. CI runs bash 5. The gap is associative arrays — here's the failure an...

shell ci-cd
Stop copy-pasting bash logging: a sourced lib with a relo...

One log.sh, sourced everywhere — plus the readonly crash that happens the second two files pull i...

shell data
jq's // default silently eats your false values

You set backup_files to false. jq read it back as true. The // operator is the culprit, and the f...

shell ci-cd
Why your CI script hangs forever: wrap --version in timeout

The script worked locally and hung in CI. The fix is one word — timeout — plus the set -e gotcha ...

git data
New Django project to first GitHub push, without committi...

Five commands to a Django project on GitHub, and the .gitignore that stops you from publishing yo...

ci-cd data
Stop the KeyError: defensively parsing tool JSON in Python

Your script read error['status']['details'] for a year. Then the tool moved the field, and CI die...

web-dev data
A reusable GPT-4o system prompt for dark-to-hopeful satir...

Stop pasting a wall of instructions into the chat box. Put the prompt in a file, build the reques...

shell ci-cd
Tangle a markdown file into runnable scripts, one file pe...

Your setup doc already contains every command. Stop copy-pasting them one block at a time — extra...

jekyll web-dev
A searchable, sortable Jekyll sitemap from your collectio...

One Liquid loop plus forty lines of vanilla JS turns your collections into a filterable, sortable...

jekyll
Build a Jekyll sidebar that lists a collection's files an...

Group a collection's docs by folder in a sidebar — and find out why the obvious pop trick prints ...

web-dev data
Document Scraping in Python: BeautifulSoup, Playwright, a...

Most data worth having is locked in HTML that was never meant to be queried. Here's the scraper, ...

shell jekyll
Front Matter Placeholders: Stop Frontmatter Drift Before ...

Templates tell authors which fields exist. Placeholders fill them in correctly — and grep finds t...

web-dev
Scaffold a VS Code extension in five minutes: yo code, F5...

The whole loop from empty folder to a working command in the palette — and the one-line mismatch ...

jekyll
A robots.txt That Actually Works on Jekyll (and the sitem...

Your robots.txt looks right and crawlers still read it wrong — because Jekyll never rendered the ...

jekyll docker
A Jekyll Dockerfile That Builds on Ruby 2.7: Pinning Past...

The fix for 'works on the new Ruby' isn't upgrading. It's pinning the four gems that quietly requ...

jekyll web-dev
Hide Your Navbar on Scroll Down, Bring It Back on Scroll Up

Reclaim the top of the viewport on scroll down, get the nav back on scroll up — and the one-pixel...