The First 38 Minutes in Claude Code (My Day-1 Ritual)

Most Claude Code tutorials skip the part that decides whether your app survives month two. They show the shiny feature build. I'm going to show you the boring 38 minutes that happens before any of that — the four-file safety net I drop into every client project before I write a single feature.
I run three production apps from one home server. None have gone down silently because of this ritual. Here's exactly how it's structured, minute by minute.
Why Day-1 discipline matters more than the feature build
The pattern I see every week with new clients looks the same. Someone vibe-codes an app with Claude Code over a weekend. It works. They demo it Monday. By Thursday, an API key is hardcoded in three places, errors are swallowed silently, there's no way to know if the thing is even running, and redeploying means SSH-ing in and copy-pasting commands they barely remember. By week three nobody trusts the system. By month two it's abandoned.
This isn't a Claude Code problem. Claude Code is fine. It's a Day-1 problem. The first hour you spend in any new project shouldn't be features — it should be the safety net that lets you sleep. No Docker. No CI/CD theater. Just four files that keep small business automations alive.
Here's what the 38 minutes actually look like:
| Minutes | File | Purpose |
|---|---|---|
| 0–8 | .env + .env.example + config.py |
Single source of truth for secrets and config |
| 8–18 | logger.py |
Rotating logs + Telegram alerts on critical errors |
| 18–28 | /health endpoint or heartbeat file |
Know if the app is alive |
| 28–35 | deploy.sh |
One-command deploy with log tail |
| 35–38 | README sanity check | Future-you instructions |
Minutes 0–8: Secrets and the single config gateway
Open the project. Create .env at the root. Immediately add it to .gitignore. This sounds obvious — half the repos I audit for new clients have API keys committed to GitHub.
Inside .env, I use the same three-block pattern across every project I ship:
# === Block 1: Credentials ===
OPENAI_API_KEY=sk-...
DATABASE_URL=postgresql://user:pass@localhost/db
TELEGRAM_BOT_TOKEN=...
TELEGRAM_ALERT_CHAT_ID=...
# === Block 2: Environment flags ===
ENV=production
LOG_LEVEL=INFO
TIMEZONE=Europe/Belgrade
# === Block 3: Feature toggles ===
ENABLE_EMAIL_DIGEST=true
ENABLE_AUTO_INVOICE=false
Then I create .env.example with the same keys but empty values. That file gets committed. It's the contract for anyone — including future-me — who clones the repo six months from now.
Next to it: config.py. This is the only place in the entire project that reads environment variables.
# config.py
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent / ".env")
# Credentials
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
DATABASE_URL = os.environ["DATABASE_URL"]
TELEGRAM_BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
TELEGRAM_ALERT_CHAT_ID = os.environ["TELEGRAM_ALERT_CHAT_ID"]
# Environment
ENV = os.getenv("ENV", "development")
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
TIMEZONE = os.getenv("TIMEZONE", "UTC")
# Feature toggles
ENABLE_EMAIL_DIGEST = os.getenv("ENABLE_EMAIL_DIGEST", "false").lower() == "true"
ENABLE_AUTO_INVOICE = os.getenv("ENABLE_AUTO_INVOICE", "false").lower() == "true"
Nothing else touches .env directly. If I need a value somewhere, I import it from config. When a key changes, I change it in one place. And when Claude Code edits the project later, I tell it in plain English in CLAUDE.md: "Never read environment variables outside config.py. Import from config." It respects that, because the structure makes it obvious.
Why this matters
- Rotating an API key is a one-line change, not a grep across the codebase.
- Feature toggles let you flip behavior off without a redeploy.
- The
.env.examplefile is documentation that doesn't lie.
Minutes 8–18: A logger that doesn't disappear at 3am
This is where weekend-built apps die. They print() to console and call it a day. The moment the app runs unattended under systemd or a cron job, those prints vanish into nothing.
I drop in one logger module. About 40 lines. Rotating file handler, timestamp + severity + function name on every line, and a try/except at the top of every entry point that logs the full traceback before exiting.
# logger.py
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
import requests
from config import LOG_LEVEL, TELEGRAM_BOT_TOKEN, TELEGRAM_ALERT_CHAT_ID, ENV
LOG_DIR = Path(__file__).parent / "logs"
LOG_DIR.mkdir(exist_ok=True)
formatter = logging.Formatter(
"%(asctime)s | %(levelname)s | %(name)s.%(funcName)s | %(message)s"
)
file_handler = RotatingFileHandler(
LOG_DIR / "app.log", maxBytes=5_000_000, backupCount=5
)
file_handler.setFormatter(formatter)
class TelegramHandler(logging.Handler):
def emit(self, record):
if record.levelno < logging.CRITICAL:
return
msg = self.format(record)
try:
requests.post(
f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage",
json={"chat_id": TELEGRAM_ALERT_CHAT_ID,
"text": f"🚨 [{ENV}] {msg[:3500]}"},
timeout=5,
)
except Exception:
pass # never let the alerter crash the app
tg_handler = TelegramHandler()
tg_handler.setFormatter(formatter)
def get_logger(name: str) -> logging.Logger:
log = logging.getLogger(name)
log.setLevel(LOG_LEVEL)
if not log.handlers:
log.addHandler(file_handler)
log.addHandler(tg_handler)
return log
Then every entry point looks like this:
from logger import get_logger
log = get_logger(__name__)
def main():
try:
run_job()
except Exception:
log.critical("Job crashed", exc_info=True)
raise
if __name__ == "__main__":
main()
For client projects that handle money — invoicing, billing, anything where a missed run costs real euros — the Telegram channel means I get a phone notification the second something explodes at 3am. Costs nothing, four lines using the bot API.
Over three months in production, this single pattern has saved me roughly six hours of debugging I would have spent staring at a black terminal trying to reproduce a bug.
Minutes 18–28: Healthcheck and external pinger
Here's the thing about a small automation: if it runs every 15 minutes and you don't watch it, you have no idea if it stopped running 30 hours ago. The script doesn't fail loudly — it just stops.
For a web app, a /health endpoint:
from fastapi import FastAPI
from datetime import datetime
import os
app = FastAPI()
START_TIME = datetime.utcnow()
@app.get("/health")
def health():
return {
"status": "ok",
"time": datetime.utcnow().isoformat(),
"uptime_seconds": (datetime.utcnow() - START_TIME).total_seconds(),
"version": os.getenv("APP_VERSION", "dev"),
}
For a cron script, write a heartbeat file at the end of every successful run:
from pathlib import Path
from datetime import datetime
HEARTBEAT = Path(__file__).parent / "heartbeat.txt"
def write_heartbeat():
HEARTBEAT.write_text(datetime.utcnow().isoformat())
Then point UptimeRobot (free tier, 5-minute interval) at the endpoint, or run a tiny watchdog script that checks the heartbeat file age. If it doesn't get a response, it sends an email and a Telegram message.
Total setup time: ~8 minutes. Total cost: zero.
This is the difference between knowing your automation is alive and assuming it's alive. For a client running an invoicing workflow, this is what makes them trust the system enough to stop checking it manually. The whole point of automation is that they stop looking. Healthcheck makes that possible.
Minutes 28–35: One-command deploy
I'm not setting up Docker for a four-file Python app that runs on one server. I'm not configuring GitHub Actions. I'm writing deploy.sh. It does five things:
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
echo "→ Pulling latest..."
git pull --ff-only
echo "→ Installing dependencies..."
source .venv/bin/activate
pip install -q -r requirements.txt
echo "→ Running migrations..."
if [ -f alembic.ini ]; then
alembic upgrade head
fi
echo "→ Restarting service..."
sudo systemctl restart myapp.service
echo "→ Last 20 log lines:"
sleep 2
tail -n 20 logs/app.log
chmod +x deploy.sh and that's it. Deploying is now ./deploy.sh. When Claude Code suggests changes, I review them, commit, push, SSH in, and run one command. The log tail at the end means I see immediately if the service came back up clean or crashed on import.
Why this beats Docker for solo projects
- Zero image build time.
- No registry to manage.
- Restart is 2 seconds, not 30.
journalctl -u myapp -fjust works.
For a single-server automation serving one business, this is the right tool. Docker is the right tool when you have 20 services across 4 environments. Don't pay that complexity tax on day one.
Minutes 35–38: The README that future-you will actually read
The last three minutes are a README with five sections, no more:
- What this does — one paragraph in plain language.
- How to run locally —
cp .env.example .env, fill it in,python main.py. - How to deploy —
./deploy.sh. - Where the logs live —
logs/app.log, rotated at 5MB, 5 backups. - What to do if it breaks — check Telegram alerts,
journalctl -u myapp -n 100, then deploy.sh after the fix.
That's it. No architecture diagrams. No badge soup. The README is for a panicked version of you at 11pm three months from now.
Why bizflowai.io helps with this
The four-file ritual above is exactly the production scaffolding we drop into every client automation at bizflowai.io before touching a single feature — secrets isolation through a single config gateway, structured logging with Telegram alerting on critical errors, a healthcheck the client never has to think about, and a one-command deploy script. It's the boring layer that turns a weekend prototype into something a small business can actually depend on for invoicing, lead follow-up, or email triage without anyone babysitting it.
The compounding cost of skipping these 38 minutes
If you skip this ritual, here's the math I see on client audits:
| Skipped step | What happens in month 2 |
|---|---|
No .env discipline |
API key leaked or rotated in 3 files; 2-4h debugging |
| No logger | Silent crash, no traceback, app dead for 36h before anyone notices |
| No healthcheck | Client manually checks system daily → loses trust → abandons it |
| No deploy script | SSH in, forget a step, half-deployed state, 30min recovery |
Thirty-eight minutes up front buys you months of not thinking about the plumbing. That's the whole point. Features are easy. The safety net is what makes the features survive.
Frequently asked questions
What is the 38-minute Day-1 ritual for new automation projects?
It's a setup routine done before writing any features, focused on four files: a .env secrets file with a committed .env.example, a single config module that reads environment variables, a logger module with error capture, and a healthcheck or heartbeat for external monitoring. The goal is a safety net that keeps small business automations alive unattended, rather than adding features that nobody will trust by week three.
How do I structure a .env file for a small automation project?
Create a .env file at the project root, add it to .gitignore immediately, and split it into three blocks: credentials (API keys, database URLs, webhook secrets), environment flags (production vs development, log level, timezone), and feature toggles you can flip without redeploying. Then commit a .env.example file with the same keys but empty values as a contract for anyone cloning the repo.
Why should only one config file read environment variables?
Centralizing environment variable access in a single config.py or config.js file means when a key changes, you update one place instead of hunting through the codebase. Every other module imports values from config rather than touching .env directly. It also makes the rule easy to enforce with Claude Code: telling it never to read environment variables outside config works because the structure is obvious.
How do I add proper logging and error capture to an automation?
Drop in a roughly 40-line logger module that writes to a rotating log file with date stamps, formats each entry with timestamp, severity, and function name, and wraps every entry point in a try-except so full tracebacks are captured instead of silent exits. Import the logger everywhere and never use print. For money-handling projects, wire critical errors to a Telegram channel via the bot API in about four lines.
How do I know if my cron job or automation stopped running?
Add a healthcheck. For web apps, expose a /health endpoint returning JSON with current time, app version, and status. For cron scripts, write a timestamp to a heartbeat file at the end of every successful run. Then use a free external pinger like UptimeRobot to check the endpoint or file every five minutes and send an email or Telegram alert if it doesn't respond.
Want more like this?
I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.
Subscribe to bizflowai.io on YouTube — never miss a new tutorial.
Planning an AI automation project or need a second opinion on your architecture?
Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.
Visit bizflowai.io for our services, case studies, and AI consulting.
Frequently asked questions
What is the 38-minute Day-1 ritual for new automation projects?
It's a setup routine done before writing any features, focused on four files: a .env secrets file with a committed .env.example, a single config module that reads environment variables, a logger module with error capture, and a healthcheck or heartbeat for external monitoring. The goal is a safety net that keeps small business automations alive unattended, rather than adding features that nobody will trust by week three.
How do I structure a .env file for a small automation project?
Create a .env file at the project root, add it to .gitignore immediately, and split it into three blocks: credentials (API keys, database URLs, webhook secrets), environment flags (production vs development, log level, timezone), and feature toggles you can flip without redeploying. Then commit a .env.example file with the same keys but empty values as a contract for anyone cloning the repo.
Why should only one config file read environment variables?
Centralizing environment variable access in a single config.py or config.js file means when a key changes, you update one place instead of hunting through the codebase. Every other module imports values from config rather than touching .env directly. It also makes the rule easy to enforce with Claude Code: telling it never to read environment variables outside config works because the structure is obvious.
How do I add proper logging and error capture to an automation?
Drop in a roughly 40-line logger module that writes to a rotating log file with date stamps, formats each entry with timestamp, severity, and function name, and wraps every entry point in a try-except so full tracebacks are captured instead of silent exits. Import the logger everywhere and never use print. For money-handling projects, wire critical errors to a Telegram channel via the bot API in about four lines.
How do I know if my cron job or automation stopped running?
Add a healthcheck. For web apps, expose a /health endpoint returning JSON with current time, app version, and status. For cron scripts, write a timestamp to a heartbeat file at the end of every successful run. Then use a free external pinger like UptimeRobot to check the endpoint or file every five minutes and send an email or Telegram alert if it doesn't respond.