
These production grade shortcuts eliminate bad patterns, boost performance, and make your code unfairly clean starting today.
A few years ago, I shipped a “simple automation script” that was supposed to rename some files and push them to S3.
It ran perfectly. For three weeks.
Then one weird filename broke the whole thing at 2AM and silently corrupted a batch of uploads. I woke up to Slack messages I never want to see again.
That was the day I stopped writing “scripts” and started writing automation like it’s production software.
Here are 14 Python hacks that changed how I build automation in 2026.
Not theory. Scars.
1. Stop Writing Scripts. Start Writing CLIs with click
If you’re still hardcoding config at the top of your file, you’re building future regret.
I used to do this:
BUCKET = "my-bucket"
DRY_RUN = TrueNow I wrap everything in a CLI using click.
import click
import boto3
@click.command()
@click.option("--bucket", required=True)
@click.option("--dry-run", is_flag=True)
def upload(bucket, dry_run):
s3 = boto3.client("s3")
if dry_run:
click.echo("Running in dry mode.")
else:
s3.upload_file("report.csv", bucket, "report.csv")
if __name__ == "__main__":
upload()Why?
Because automation evolves. CLI flags force clarity.
Takeaway: If it runs twice, give it flags.
2. Use concurrent.futures Like You Actually Mean It
Most automation scripts are I/O bound.
Stop looping over APIs sequentially.
from concurrent.futures import ThreadPoolExecutor
import requests
urls = ["https://api.site1.com", "https://api.site2.com"]
def fetch(url):
return requests.get(url, timeout=5).json()
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch, urls))
print(results)The first time I parallelized a scraping tool, runtime dropped from 9 minutes to 1.4 minutes.
And yes, I felt stupid.
Takeaway: If your script waits on the network, threads are free performance.
3. Use pathlib. Always.
os.path.join is not charming. It’s legacy trauma.
from pathlib import Path
base = Path("data")
for file in base.glob("*.csv"):
print(file.read_text())Path objects feel like objects, not string manipulation hacks.
Takeaway: Treat paths like data types, not strings.
4. Replace Cron Jobs with schedule in Code
I used to maintain server crontabs.
Never again.
import schedule
import time
def job():
print("Running backup...")
schedule.every().day.at("02:00").do(job)
while True:
schedule.run_pending()
time.sleep(1)It lives in the repo. Versioned. Reviewed.
That alone is worth it.
Takeaway: If it matters, it belongs in code, not a forgotten server config.
5. Retry Like a Grown-Up with tenacity
APIs fail. Your script shouldn’t panic.
from tenacity import retry, stop_after_attempt, wait_fixed
import requests
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
def fetch_data():
return requests.get("https://api.example.com").json()
print(fetch_data())Before this, I wrote ugly try/except loops everywhere.
Tenacity made retries declarative. Clean.
Takeaway: Transient errors deserve strategy, not spaghetti.
6. Automate the Browser with selenium — But Headless and Controlled
If you’re scraping dashboards manually… why?
from selenium import webdriver
from selenium.webdriver.common.by import By
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com/login")
driver.find_element(By.ID, "username").send_keys("user")
driver.quit()My rule: if a task involves logging into a dashboard more than twice, automate it.
But throttle. Add waits. Respect rate limits.
Takeaway: Automate humans out of repetitive web tasks.
7. Use sqlite3 for Temporary State
I used to dump JSON files everywhere.
Chaos.
Now I use SQLite for quick local state.
import sqlite3
conn = sqlite3.connect("jobs.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS jobs(id TEXT, status TEXT)")
cur.execute("INSERT INTO jobs VALUES (?, ?)", ("123", "pending"))
conn.commit()Zero setup. ACID. Reliable.
Takeaway: Files lie. Databases don’t.
8. Use subprocess.run Properly
I once used os.system() in production.
We don’t talk about that.
import subprocess
result = subprocess.run(
["ls", "-l"],
capture_output=True,
text=True,
check=True
)
print(result.stdout)You get structured results. Error handling. Control.
Takeaway: If you call shell tools, treat them like APIs.
9. Detect Changes with filecmp Before Doing Work
Don’t reprocess files blindly.
import filecmp
if not filecmp.cmp("new.csv", "old.csv", shallow=False):
print("File changed. Processing...")Saved me hours of unnecessary ETL runs.
Takeaway: Automation isn’t about doing more. It’s about doing less intelligently.
10. Validate Environment with python-dotenv
You think your environment variables are set.
They’re not.
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.getenv("API_KEY")
if not api_key:
raise RuntimeError("Missing API_KEY")Fail early. Loudly.
Takeaway: Silent misconfiguration is worse than crashing.
11. Use tqdm for Visibility
Automation without progress bars feels broken.
from tqdm import tqdm
import time
for _ in tqdm(range(100)):
time.sleep(0.05)When users see progress, they trust your script.
Takeaway: Observability isn’t optional — even in small tools.
12. Use zipfile Instead of Shelling Out
You don’t need zip installed.
import zipfile
with zipfile.ZipFile("archive.zip", "w") as z:
z.write("report.csv")Portable. Cross-platform.
Takeaway: Fewer external dependencies = fewer surprises.
13. Build Idempotency Into Everything
This one hurts.
If your script runs twice and breaks something, that’s on you.
from pathlib import Path
output = Path("output.txt")
if not output.exists():
output.write_text("Processed")Every automation I write now assumes it will run twice.
Because it will.
Takeaway: Idempotency is not optional in 2026.
14. Profile Before You Optimize
I once rewrote a script to use multiprocessing.
The bottleneck was JSON parsing.
Use cProfile.
import cProfile
import requests
def run():
for _ in range(100):
requests.get("https://httpbin.org/get")
cProfile.run("run()")Guessing is expensive. Measuring is cheap.
Takeaway: Your intuition about performance is usually wrong.
One Opinion Most Tutorials Won’t Say
If your automation script is more than 150 lines and has no structure, tests, or logging…
It’s not a script.
It’s a liability.
The older I get as a Python engineer, the less I care about cleverness and the more I care about survivability.
Clean automation feels boring.
Boring is good.
Because boring doesn’t wake you up at 2AM.
And if you’ve ever been woken up by a “simple script,” you already know exactly what I mean.
Writer : Ahmad Nadeem
— Bhuwan Chettri
Editor, CodeToDeploy
CodeToDeploy Is a Tech-Focused Publication Helping Students, Professionals, And Creators Stay Ahead with AI, Coding, Cloud, Digital Tools, And Career Growth Insights.