# PortOpt — Best Practices Reference This document lists every best-practice rule to follow for this project going forward. Organised by layer. Each rule states **what** to do, **why**, and **when it applies**. --- ## 1. Git & Version Control | # | Rule | Why | |---|------|-----| | 1 | Add `.gitignore` **before** the first commit | Binary/generated files (`.db`, `__pycache__`, `.env`) are hard to purge from history once committed | | 2 | Never commit secrets or local config (`.env`, credentials) | Leaked keys cannot be un-leaked | | 3 | Verify `git config user.email` matches your GitHub account before pushing | Vercel blocks deployments from unrecognised committers | | 4 | Use feature branches; merge to `main` via PR | Protects main from broken pushes; gives a review checkpoint | | 5 | Prefer a new commit over force-push to trigger Vercel builds | Vercel's webhook may ignore a force-push even if history changed | | 6 | Use concise, imperative commit messages (`Fix`, `Add`, `Remove`) | GitHub squashes long bodies into subject-only views | --- ## 2. Dependency Management | # | Rule | Why | |---|------|-----| | 7 | Pin **all** deps (including transitive) in `requirements.txt` for Vercel | Vercel Python 3.12 doesn't auto-install transitive deps at runtime | | 8 | Use upper-bound pins for fast-moving libs (`cvxpy<2`, `numpy<2`) | Major version breaks are common; upper bounds prevent surprise failures | | 9 | Add a `.python-version` file (`3.12`) | Documents the exact runtime; tools like pyenv respect it | | 10 | Use a virtual environment locally (`python -m venv .venv`) | Prevents version conflicts with system packages | --- ## 3. Flask Application Structure | # | Rule | Why | |---|------|-----| | 11 | Split the app into `services/` (business logic) and `routes/` (Flask Blueprints) | A 850-line `app.py` is unreadable and untestable | | 12 | Register Blueprints in `app.py`; keep `app.py` under ~30 lines | The entry point should just wire things up, not contain logic | | 13 | Never use `warnings.filterwarnings("ignore")` globally | Masks real bugs; scope it to the specific module/category that needs it | | 14 | Use `logging` instead of `print()` / `traceback.print_exc()` | Serverless logs need structured output; print goes to stdout with no level | | 15 | Add a `/health` endpoint that returns `{"status": "ok"}` | Required by load balancers, uptime monitors, and Vercel warm-pings | --- ## 4. Input Validation & Error Handling | # | Rule | Why | |---|------|-----| | 16 | Call `request.get_json(silent=True)` and check for `None` before accessing keys | Bare `request.json["key"]` throws `TypeError` or `KeyError` on malformed input | | 17 | Validate all required fields at the top of each route; return **400** for bad input | Routes that accept whatever they get silently fail or crash | | 18 | Return correct HTTP status codes: 400 for bad input, 500 for server errors, 404 for not found | A 200 response with `{"error": "..."}` confuses monitoring tools and CDNs | | 19 | Catch **specific** exceptions (`ValueError`, `KeyError`) not bare `except Exception` | Bare except hides bugs; you can't distinguish expected failures from crashes | | 20 | Log the full traceback server-side; return only a safe error message to the client | Stack traces in API responses leak internal structure | --- ## 5. Database (SQLite / db.py) | # | Rule | Why | |---|------|-----| | 21 | Never use `PRAGMA journal_mode=WAL` in serverless | WAL requires `-shm`/`-wal` shared-memory files that don't exist in Vercel/Lambda containers | | 22 | Replace `datetime.utcnow()` with `datetime.now(timezone.utc)` | `utcnow()` is deprecated in Python 3.12 and will be removed in 3.14 | | 23 | Wrap `init_db()` in a try/except; don't let import-time side-effects crash the module | An `init_db()` failure at import kills the entire Lambda cold start with a cryptic error | | 24 | **SQLite in `/tmp` is ephemeral on Vercel** — saved data is lost on every cold start | Vercel's container filesystem is wiped between cold starts; use a real DB for persistence | | 25 | For production persistence on serverless: use Supabase (PostgreSQL) or move to Railway/Render | These provide either a managed DB or a persistent disk without changing the Flask code much | --- ## 6. Frontend (HTML/CSS/JS) | # | Rule | Why | |---|------|-----| | 26 | Split a 2000-line `index.html` into `static/css/app.css` and `static/js/app.js` | One file is unmaintainable; split enables browser caching and better diffs | | 27 | Add a `` (favicon) | Browsers make a request for `/favicon.ico` on every load; 404 is a log noise source | | 28 | Add Subresource Integrity (SRI) hashes to CDN `