AI Coding Assistants: A Practical Guide to Actually Using Them Well

Everyone has access to AI coding assistants now. Most people use them poorly. Here’s how to actually get value from them. The Mental Model Shift Stop thinking of AI assistants as “autocomplete on steroids.” Think of them as a junior developer who: Has read every Stack Overflow answer ever written Types infinitely fast Never gets tired or annoyed Has no memory of what you discussed 5 minutes ago Will confidently produce plausible-looking nonsense That last point is crucial. These tools don’t know things. They predict likely tokens. The output often looks right even when it’s wrong. ...

March 12, 2026 Â· 9 min Â· 1750 words Â· Rob Washington

curl Tips and Tricks for API Work

Every developer ends up using curl. It’s installed everywhere, works with any API, and once you learn the patterns, it’s faster than any GUI tool. Basic Requests 1 2 3 4 5 6 7 8 9 10 11 # GET (default) curl https://api.example.com/users # POST with data curl -X POST https://api.example.com/users \ -d '{"name":"Alice"}' # Other methods curl -X PUT ... curl -X DELETE ... curl -X PATCH ... Headers 1 2 3 4 5 6 7 8 9 10 # Set content type curl -H "Content-Type: application/json" ... # Auth header curl -H "Authorization: Bearer TOKEN" ... # Multiple headers curl -H "Content-Type: application/json" \ -H "Authorization: Bearer TOKEN" \ -H "X-Custom-Header: value" ... JSON Data 1 2 3 4 5 6 7 8 9 10 11 12 # Inline JSON curl -X POST https://api.example.com/users \ -H "Content-Type: application/json" \ -d '{"name":"Alice","email":"alice@example.com"}' # From file curl -X POST https://api.example.com/users \ -H "Content-Type: application/json" \ -d @data.json # Pretty output (pipe to jq) curl -s https://api.example.com/users | jq Response Info 1 2 3 4 5 6 7 8 9 10 11 # Show response headers curl -i https://api.example.com/users # Only headers (no body) curl -I https://api.example.com/users # HTTP status code only curl -s -o /dev/null -w "%{http_code}" https://api.example.com/users # Timing info curl -w "Time: %{time_total}s\n" -o /dev/null -s https://api.example.com Authentication 1 2 3 4 5 6 7 8 9 10 11 # Basic auth curl -u username:password https://api.example.com # Bearer token curl -H "Authorization: Bearer eyJ..." https://api.example.com # API key in header curl -H "X-API-Key: abc123" https://api.example.com # API key in query curl "https://api.example.com?api_key=abc123" Form Data 1 2 3 4 5 6 7 8 # URL-encoded form curl -X POST https://example.com/login \ -d "username=alice&password=secret" # Multipart form (file upload) curl -X POST https://example.com/upload \ -F "file=@photo.jpg" \ -F "description=My photo" Following Redirects 1 2 3 4 5 # Follow redirects (3xx) curl -L https://example.com/redirect # Show redirect chain curl -L -v https://example.com/redirect 2>&1 | grep "< HTTP\|< Location" SSL/TLS 1 2 3 4 5 6 7 8 # Ignore SSL errors (dev only!) curl -k https://self-signed.example.com # Use specific cert curl --cacert /path/to/ca.crt https://api.example.com # Client cert authentication curl --cert client.crt --key client.key https://api.example.com Timeouts 1 2 3 4 5 6 7 8 # Connection timeout (seconds) curl --connect-timeout 5 https://api.example.com # Max time for entire operation curl --max-time 30 https://api.example.com # Both curl --connect-timeout 5 --max-time 30 https://api.example.com Debugging 1 2 3 4 5 6 7 8 # Verbose output curl -v https://api.example.com # Even more verbose (includes SSL handshake) curl -vv https://api.example.com # Trace everything to file curl --trace trace.log https://api.example.com Saving Output 1 2 3 4 5 6 7 8 # Save to file curl -o response.json https://api.example.com/data # Save with remote filename curl -O https://example.com/file.zip # Silent mode (no progress) curl -s https://api.example.com Cookies 1 2 3 4 5 6 7 8 # Send cookie curl -b "session=abc123" https://example.com # Save cookies to file curl -c cookies.txt https://example.com/login -d "user=alice" # Send cookies from file curl -b cookies.txt https://example.com/dashboard Retry Logic 1 2 3 4 5 6 7 8 # Retry on failure curl --retry 3 https://api.example.com # Retry with delay curl --retry 3 --retry-delay 2 https://api.example.com # Retry on specific errors curl --retry 3 --retry-connrefused https://api.example.com Useful Aliases Add to your .bashrc: ...

March 5, 2026 Â· 4 min Â· 810 words Â· Rob Washington

jq: Command-Line JSON Processing

APIs return JSON. Config files use JSON. Logs are often JSON. The jq tool lets you slice, filter, and transform JSON from the command line—no scripting required. Basic Syntax 1 2 3 4 5 jq 'filter' input.json # or cat input.json | jq 'filter' # or curl -s api.example.com | jq 'filter' Pretty Print 1 2 3 4 5 6 7 8 9 10 11 # Ugly JSON echo '{"name":"Alice","age":30}' | jq # Output: # { # "name": "Alice", # "age": 30 # } # Compact (remove whitespace) echo '{"name": "Alice"}' | jq -c # {"name":"Alice"} Extract Fields 1 2 3 4 5 6 echo '{"name":"Alice","age":30}' | jq '.name' # "Alice" # Without quotes echo '{"name":"Alice","age":30}' | jq -r '.name' # Alice Nested Objects 1 2 echo '{"user":{"name":"Alice","email":"alice@example.com"}}' | jq '.user.name' # "Alice" Arrays 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # Get element by index echo '[1,2,3,4,5]' | jq '.[0]' # 1 # Get slice echo '[1,2,3,4,5]' | jq '.[1:3]' # [2, 3] # Get all elements echo '[{"name":"Alice"},{"name":"Bob"}]' | jq '.[].name' # "Alice" # "Bob" # Wrap in array echo '[{"name":"Alice"},{"name":"Bob"}]' | jq '[.[].name]' # ["Alice", "Bob"] Filtering 1 2 3 4 5 6 # Select objects matching condition echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | jq '.[] | select(.age > 26)' # {"name": "Alice", "age": 30} # Multiple conditions jq '.[] | select(.status == "active" and .role == "admin")' Transform Data 1 2 3 4 5 6 7 8 9 10 11 # Create new object echo '{"first":"Alice","last":"Smith"}' | jq '{fullName: "\(.first) \(.last)"}' # {"fullName": "Alice Smith"} # Map over array echo '[1,2,3]' | jq 'map(. * 2)' # [2, 4, 6] # Add field echo '{"name":"Alice"}' | jq '. + {role: "admin"}' # {"name": "Alice", "role": "admin"} Real-World Examples Parse API response: ...

March 5, 2026 Â· 5 min Â· 955 words Â· Rob Washington

Terminal Productivity: Tools and Techniques for Speed

The terminal is where developers live. A few minutes learning shortcuts and tools saves hours over a career. These are the techniques that make the biggest difference. Shell Basics That Pay Off History Navigation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # Search history with Ctrl+R # Type partial command, press Ctrl+R repeatedly to cycle # Run last command !! # Run last command with sudo sudo !! # Run last command starting with 'git' !git # Replace text in last command ^typo^fixed # If last command was: git psuh # ^psuh^push runs: git push # Last argument of previous command cd /very/long/path/to/directory ls $_ # $_ = /very/long/path/to/directory Keyboard Shortcuts 1 2 3 4 5 6 7 8 Ctrl+A # Move to beginning of line Ctrl+E # Move to end of line Ctrl+U # Delete from cursor to beginning Ctrl+K # Delete from cursor to end Ctrl+W # Delete word before cursor Alt+B # Move back one word Alt+F # Move forward one word Ctrl+L # Clear screen (keeps current line) Brace Expansion 1 2 3 4 5 6 7 8 9 10 11 # Create multiple directories mkdir -p project/{src,tests,docs} # Create multiple files touch file{1,2,3}.txt # Backup a file cp config.yml{,.bak} # Creates config.yml.bak # Rename with pattern mv file.{txt,md} # Renames file.txt to file.md Essential Tools fzf - Fuzzy Finder 1 2 3 4 5 6 7 8 9 10 11 12 13 # Install brew install fzf # macOS apt install fzf # Debian/Ubuntu # Search files fzf # Pipe anything to fzf cat file.txt | fzf ps aux | fzf # Ctrl+R replacement (add to .bashrc/.zshrc) # Much better history search ripgrep (rg) - Fast Search 1 2 3 4 5 6 7 8 9 10 11 12 13 # Install brew install ripgrep # Search in files (faster than grep) rg "pattern" rg "TODO" --type py rg "function" -g "*.js" # Case insensitive rg -i "error" # Show context rg -C 3 "exception" # 3 lines before and after fd - Better Find 1 2 3 4 5 6 7 8 9 10 # Install brew install fd # Find files fd "pattern" fd -e py # Find .py files fd -t d # Find directories only # Execute on results fd -e log -x rm # Delete all .log files bat - Better Cat 1 2 3 4 5 6 7 8 # Install brew install bat # Syntax highlighting, line numbers, git changes bat file.py # Use as man pager export MANPAGER="sh -c 'col -bx | bat -l man -p'" eza/exa - Better ls 1 2 3 4 5 6 7 8 # Install brew install eza # Tree view with git status eza --tree --level=2 --git # Long format with icons eza -la --icons jq - JSON Processing 1 2 3 4 5 6 7 8 9 10 11 # Pretty print JSON cat data.json | jq . # Extract field curl api.example.com/users | jq '.[0].name' # Filter array jq '.[] | select(.active == true)' # Transform jq '{name: .user.name, email: .user.email}' Tmux Essentials 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # Start new session tmux new -s project # Detach: Ctrl+B, then D # List sessions tmux ls # Attach to session tmux attach -t project # Split panes Ctrl+B % # Vertical split Ctrl+B " # Horizontal split Ctrl+B o # Switch pane Ctrl+B z # Zoom pane (toggle) # Windows Ctrl+B c # New window Ctrl+B n # Next window Ctrl+B p # Previous window Ctrl+B , # Rename window Git Shortcuts 1 2 3 4 5 6 7 8 9 10 11 # .gitconfig [alias] s = status -sb co = checkout br = branch ci = commit ca = commit --amend lg = log --oneline --graph --decorate -20 unstage = reset HEAD -- last = log -1 HEAD aliases = config --get-regexp alias 1 2 3 4 5 6 # Common workflows git s # Short status git lg # Pretty log git co - # Switch to previous branch git stash -u # Stash including untracked git stash pop # Apply and remove stash Shell Aliases 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # Add to .bashrc or .zshrc # Navigation alias ..="cd .." alias ...="cd ../.." alias ~="cd ~" # Safety alias rm="rm -i" alias mv="mv -i" alias cp="cp -i" # Shortcuts alias ll="ls -lah" alias la="ls -la" alias g="git" alias k="kubectl" alias d="docker" alias dc="docker compose" # Quick edits alias zshrc="$EDITOR ~/.zshrc" alias reload="source ~/.zshrc" # Common tasks alias ports="netstat -tulanp" alias myip="curl ifconfig.me" alias weather="curl wttr.in" Functions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # Make directory and cd into it mkcd() { mkdir -p "$1" && cd "$1" } # Extract any archive extract() { case $1 in *.tar.gz) tar xzf "$1" ;; *.tar.bz2) tar xjf "$1" ;; *.tar.xz) tar xJf "$1" ;; *.zip) unzip "$1" ;; *.gz) gunzip "$1" ;; *) echo "Unknown format: $1" ;; esac } # Find and kill process by name killnamed() { ps aux | grep "$1" | grep -v grep | awk '{print $2}' | xargs kill -9 } # Quick HTTP server serve() { python3 -m http.server ${1:-8000} } # Git clone and cd gclone() { git clone "$1" && cd "$(basename "$1" .git)" } Quick One-Liners 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # Find large files find . -type f -size +100M # Disk usage by directory du -sh */ | sort -h # Watch command output watch -n 2 'kubectl get pods' # Run command on file change while inotifywait -e modify file.py; do python file.py; done # Parallel execution cat urls.txt | xargs -P 10 -I {} curl -s {} # Quick calculations echo $((2**10)) # 1024 python3 -c "print(2**100)" # Generate password openssl rand -base64 32 # Quick timestamp date +%Y%m%d_%H%M%S Environment Management 1 2 3 4 5 6 # .envrc with direnv # Auto-load environment per directory echo 'export API_KEY=xxx' > .envrc direnv allow # Now API_KEY is set when you cd into this directory SSH Config 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ~/.ssh/config Host dev HostName dev.example.com User deploy IdentityFile ~/.ssh/dev_key Host prod HostName prod.example.com User deploy ProxyJump bastion Host * AddKeysToAgent yes IdentitiesOnly yes # Now just: ssh dev Prompt Customization 1 2 3 4 5 # Starship - cross-shell prompt brew install starship echo 'eval "$(starship init zsh)"' >> ~/.zshrc # Shows git status, language versions, errors, duration The Power Combo My essential setup: ...

February 24, 2026 Â· 7 min Â· 1294 words Â· Rob Washington

Makefiles for Project Automation: Simple, Portable Task Running

Make is 47 years old and still useful. Not for building C programs (though it does that), but as a simple task runner. Every Unix system has it. No installation required. One file defines all your project commands. Basic Structure 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # Makefile .PHONY: help build test clean help: @echo "Available targets:" @echo " build - Build the application" @echo " test - Run tests" @echo " clean - Clean build artifacts" build: npm run build test: npm test clean: rm -rf dist/ node_modules/ 1 2 3 make build make test make clean .PHONY Explained Without .PHONY, make checks if a file named “build” exists: ...

February 24, 2026 Â· 6 min Â· 1264 words Â· Rob Washington

AI Coding Assistants: From Skeptic to True Believer

How AI coding assistants transformed my workflow — and why the skeptics are missing out.

February 10, 2026 Â· 3 min Â· 515 words Â· Rob Washington