Hello, world — and why I'm writing
A first post about what this blog is for, who it's for, and the kind of writing I want to do here. With a few real examples of what the rendering looks like.
I've been meaning to write more for a while. Most engineers I admire have a place where they think out loud — not a polished publication, just a corner of the internet where half-formed ideas are allowed to live until they're worth keeping. This is mine.
What this blog is for
I work on production ML and GenAI systems at Volvo Group — warranty AI, an enterprise GenAI assistant, the kind of work that lives in a monorepo and never tweets itself. A lot of what I learn doesn't fit in a paper or a LinkedIn post, but it's useful to someone. Things like:
- The unglamorous tradeoffs you make when retrieval, rules, and an LLM all need to agree
- How a feature pipeline actually fails on a Tuesday
- Where a "naive RAG" stops being naive
- Why your text-to-SQL agent works in evals and dies on the first user
Posts will be short when I have something compact to say, and long when I need to draw a diagram.
What it won't be
This isn't a tutorial site. There's no "10 things you must know about LLMs". I'd rather write one honest post about a thing that bit me than ten over-confident posts about a thing I read about.
Quick design tour
Because this is the very first post on a brand-new blog, the rest of this file is mostly here to make sure the rendering pipeline works. If you're here as a human reader: just skim.
Headings, bold, and links
Markdown does the obvious things. Bold is bold, italic is italic, and external links open in a new tab, while internal links stay in the SPA.
Lists
Ordered lists for steps:
- Frame the business problem in one sentence.
- Inventory the data; profile it before modelling.
- Build the dumbest baseline you can defend.
- Make the second-dumbest one beat it.
Unordered lists for grab-bags:
- PySpark + Unity Catalog for batch pipelines
- MLflow for experiment tracking and the model registry
- Azure Functions + Service Bus for async inference
- Caddy + Docker for everything user-facing
Blockquote
Make it work, make it right, make it fast — in that order. Production ML has a fourth step: make it explainable.
A small table
| Layer | Tool | Why |
|---|---|---|
| Data | Databricks + Unity Catalog | Governance, lineage, cheap compute |
| Training | MLflow on Databricks | Tracking + registry in one place |
| Serving | Azure Functions | Async, scales to zero |
| Routing | FastAPI on App Service | One synchronous entry point |
Code, inline and fenced
A bit of inline code: if probability > threshold: route_to_human().
A real block, Python:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Claim(BaseModel):
id: str
text: str
vehicle: str
@app.post("/score")
def score(claim: Claim):
# 1. deterministic rules first — cheap and explainable
if rule_engine.reject(claim):
return {"decision": "reject", "by": "rules"}
# 2. fall back to the GBM
p = gbm.predict_proba(claim)[1]
if p < 0.2 or p > 0.8:
return {"decision": "auto", "score": float(p), "by": "ml"}
# 3. only the hard ones reach the LLM agent
return agent.evaluate(claim)
A bit of SQL too:
SELECT date_trunc('day', created_at) AS day,
count(*) FILTER (WHERE decision = 'auto') AS auto_decisions,
avg(latency_ms) AS p_avg_latency
FROM warranty_inference
WHERE created_at > now() - interval '7 days'
GROUP BY 1
ORDER BY 1;
That's everything the renderer needs to handle for now. If you're reading this and something looks off — wrong color on a token, awkward spacing around a heading, a heading that doesn't breathe — let me know and I'll fix it before the next post.
— Erfan