Get new posts by email

New posts on building and shipping products — data, AI and the engineering behind them, straight to your inbox.

It's completely free, and you can unsubscribe anytime.

By subscribing you agree to Substack's Terms of Use, its Privacy Policy and its Information collection notice.

Substack

Hi, I’m Vishnujan Narayanan.Your friendly neighbourhooddeveloper.

Writing

Blogs

Notes from the build — data pipelines, ETL, scraping and the backend and DevOps work that keeps products up in production.

Testing

How to Test a Data Pipeline: What Tests Miss

A green suite is evidence about your code, not your data. The cheap tests that catch what it misses.

Read →
Data engineering

Data Ingestion Bugs and How to Catch Them

Five ways ingestion breaks, with the guard for each. Eight of about 30 bugs threw no error at all.

Read →
Web scraping

How to Scrape a Site That Paginates by Date

Find the undocumented window cap, resume from behind, and dedup on a key that cannot fail silently.

Read →
LLMs

Local LLM vs API: When to Run Your Own Model

What fits in 6GB, what it costs in latency, and the schema trick that makes a small model usable.

Read →
ETL

How to Make an ETL Pipeline Safe to Rerun

A rerun should repair data, not corrupt it. Four ways that rule breaks, and a six-point checklist.

Read →
APIs

HTTP 429: What It Means and How to Retry

One status code, three meanings — wait, stop, or out of quota. How to tell them apart in code.

Read →
Quant / ML

Can You Predict Stock Prices Minute by Minute?

Four checks that decide whether a short-horizon model has an edge. One real attempt failed all four.

Read →
Web scraping

Why Your Scraper Gets Blocked and How to Fix It

Four layers block you: headers, TLS fingerprint, IP, behaviour. How to tell which one, and the fix for each.

Read →
Web scraping

Playwright vs Selenium: Which to Use and When

Locators, saved sessions and pinned browsers — plus the 52 fixed sleeps that show what auto-waiting does not fix.

Read →
15 rows in set (0.001 sec)

The Workflow

PipeLine

Ingest & store.Serve.Ship & run.

Every product I build runs on the same data backbone: scrapers and APIs feed it, PostgreSQL holds it, FastAPI serves it, Docker and CI/CD ship it. This is that workflow, stage by stage, with the work that proves it.

QuickForge, a product built on this workflow

Sources change
without telling you.

Ingest. Sites paginate, rate-limit and rename fields without warning. A pipeline that trusts its source fails silently.

How I handle it

Python and Playwright scrapers with retry logic that reads what a 429 actually means, and a schema check on every load. 28 pipelines across ~1,665 listed companies.

See it: Market Data Platform · HTTP 429 retries

A rerun should
repair data, not corrupt it.

Store. Jobs crash halfway. Backfills overlap. If a second run doubles rows, nobody trusts the numbers again.

How I handle it

PostgreSQL with idempotent upserts and incremental, resumable loads, so any job can be killed and rerun safely.

See it: Resumable ETL · Scraping NSE filings

Data nobody can query
is dead weight.

Serve. The pipeline is only worth something once a product, a model or a person can reach what it collected.

How I handle it

FastAPI services in front of the data, Redis for the hot paths, pgvector when search has to understand meaning.

See it: Fraud Scoring App · Bookshop Website

Green tests
don’t mean good data.

Ship & run. Code passes CI and still loads garbage. Eight of about 30 real ingestion bugs threw no error at all.

How I handle it

Docker images, CI/CD on every push, jobs running on Linux and AWS, and data checks that catch the failures that stay quiet.

See it: Ingestion bugs · Testing a pipeline

Built on this stack · Lab 01

QuickForge

Real-time job ingestion, pgvector matching and Telegram alerts: the same four stages, applied to a product. In development.

PythonPlaywrightPostgreSQL + pgvectorFastAPIRedisDockerCI/CDAWSLinuxTelegram