[{"data":1,"prerenderedAt":342},["ShallowReactive",2],{"writing-index-en":3,"writing-page-en":279},[4,98,143,185,239],{"id":5,"title":6,"bars":7,"blurb":8,"body":9,"color":7,"contacts":7,"date":82,"dateModified":7,"description":83,"extension":84,"featured":85,"groups":7,"kicker":86,"meta":87,"metrics":7,"n":88,"navigation":85,"openTabs":7,"path":89,"progress":7,"readTime":90,"reading":7,"role":7,"rules":7,"running":7,"seo":91,"ships":7,"slug":92,"stack":7,"started":7,"status":7,"stem":93,"streak":7,"tag":94,"tagColor":96,"tagline":7,"tasks":7,"timeline":7,"topics":7,"week":7,"year":7,"__hash__":97},"en\u002Fwriting\u002F014-matchpro-engine.md","Rewriting matchpro's matching engine",null,"What I learned from watching recruiters click for three weeks.",{"type":10,"value":11,"toc":76},"minimark",[12,16,20,25,28,31,41,70,73],[13,14,15],"p",{},"▸ featured · case study — № 014 · apr 18 '26 · 8 min",[17,18,6],"h2",{"id":19},"rewriting-matchpros-matching-engine",[13,21,22],{},[23,24,8],"em",{},[26,27],"hr",{},[13,29,30],{},"for three years matchpro matched candidates to jobs the lazy way — a tf-idf over titles, with a sprinkle of rules. it was fine. not great. recruiters kept emailing me screenshots of obvious matches it missed. so this spring i sat down and rebuilt the whole thing.",[32,33,34],"blockquote",{},[13,35,36,37,40],{},"\"matching is not search. search rewards precision. matching rewards being ",[23,38,39],{},"interestingly close",".\"",[42,43,44,52,58,64],"ol",{},[45,46,47,51],"li",{},[48,49,50],"strong",{},"throw away the ranker"," — kept the data, dropped the model. started from a blank ipynb.",[45,53,54,57],{},[48,55,56],{},"embed everything"," — résumé + job text → small multilingual model, cached in pg_vector.",[45,59,60,63],{},[48,61,62],{},"re-rank with signals"," — location, seniority, years-in-stack. boring features still win.",[45,65,66,69],{},[48,67,68],{},"shadow-run for 3 weeks"," — a\u002Fb on recruiters' clicks. watched dashboards more than family.",[13,71,72],{},"the numbers after 11 weeks: match-quality 41% → 78%, time-to-first-click 9.2s → 3.1s, weekly placements 12 → 28, p95 latency 640ms → 180ms.",[13,74,75],{},"read the full postmortem on \u002Fwork\u002Fmatchpro.",{"title":77,"searchDepth":78,"depth":79,"links":80},"",2,3,[81],{"id":19,"depth":78,"text":6},"2026-04-18","Rewriting matchpro's matching engine — what I learned from watching recruiters click for three weeks.","md",true,"case study",{},"014","\u002Fwriting\u002F014-matchpro-engine","8 min",{"title":6,"description":83},"014-matchpro-engine","writing\u002F014-matchpro-engine",{"label":95,"color":96},"long read","var(--red)","zKM9k3xbKEs_rywRELI7EKj4Y_-0H1SfQ1_9haVJdH8",{"id":99,"title":100,"bars":7,"blurb":101,"body":102,"color":7,"contacts":7,"date":128,"dateModified":7,"description":129,"extension":84,"featured":130,"groups":7,"kicker":131,"meta":132,"metrics":7,"n":133,"navigation":85,"openTabs":7,"path":134,"progress":7,"readTime":135,"reading":7,"role":7,"rules":7,"running":7,"seo":136,"ships":7,"slug":137,"stack":7,"started":7,"status":7,"stem":138,"streak":7,"tag":139,"tagColor":141,"tagline":7,"tasks":7,"timeline":7,"topics":7,"week":7,"year":7,"__hash__":142},"en\u002Fwriting\u002F013-support-email.md","Why I still answer every support email myself","A short defense of doing customer support on purpose.",{"type":10,"value":103,"toc":125},[104,107,110,114,116,119,122],[13,105,106],{},"№ 013 · apr 02 '26 · 4 min · operations",[17,108,100],{"id":109},"why-i-still-answer-every-support-email-myself",[13,111,112],{},[23,113,101],{},[26,115],{},[13,117,118],{},"every reply goes out under my name. not a shared inbox, not a tier-1 script, not a chatbot with a smiley avatar.",[13,120,121],{},"people tell me this doesn't scale. they're right — it doesn't. that's the point. support is where you find out which of your clever features break on a tuesday morning for a real human in a real timezone. i'd rather read those emails myself than read a weekly summary.",[13,123,124],{},"the group is small on purpose. a founder's inbox is a moat, not a cost center.",{"title":77,"searchDepth":78,"depth":79,"links":126},[127],{"id":109,"depth":78,"text":100},"2026-04-02","Why I still answer every support email myself — a short defense of doing customer support on purpose.",false,"operations",{},"013","\u002Fwriting\u002F013-support-email","4 min",{"title":100,"description":129},"013-support-email","writing\u002F013-support-email",{"label":140,"color":141},"notebook","var(--amber)","jFeELVb3z-J36SSXjiJYoGGvuT5cWLYSqOuOKQKs_k4",{"id":144,"title":145,"bars":7,"blurb":146,"body":147,"color":7,"contacts":7,"date":173,"dateModified":7,"description":174,"extension":84,"featured":130,"groups":7,"kicker":175,"meta":176,"metrics":7,"n":177,"navigation":85,"openTabs":7,"path":178,"progress":7,"readTime":179,"reading":7,"role":7,"rules":7,"running":7,"seo":180,"ships":7,"slug":181,"stack":7,"started":7,"status":7,"stem":182,"streak":7,"tag":183,"tagColor":141,"tagline":7,"tasks":7,"timeline":7,"topics":7,"week":7,"year":7,"__hash__":184},"en\u002Fwriting\u002F012-shipping-silently.md","On shipping things when no one is watching","Slack notifications off. Keyboard on. Trust the calendar.",{"type":10,"value":148,"toc":170},[149,152,155,159,161,164,167],[13,150,151],{},"№ 012 · mar 20 '26 · 3 min · process",[17,153,145],{"id":154},"on-shipping-things-when-no-one-is-watching",[13,156,157],{},[23,158,146],{},[26,160],{},[13,162,163],{},"most weeks, nothing dramatic happens. a backend migration lands. a typo gets fixed. a small feature ships behind a flag to eleven users.",[13,165,166],{},"it's tempting to wait for an audience before you do anything. to tie each release to a launch tweet, a newsletter, a post. the quiet weeks are where the products actually get built.",[13,168,169],{},"calendar says ship on friday. so ship on friday.",{"title":77,"searchDepth":78,"depth":79,"links":171},[172],{"id":154,"depth":78,"text":145},"2026-03-20","On shipping things when no one is watching — slack off, keyboard on, trust the calendar.","process",{},"012","\u002Fwriting\u002F012-shipping-silently","3 min",{"title":145,"description":174},"012-shipping-silently","writing\u002F012-shipping-silently",{"label":140,"color":141},"99k-p5IV-GKvs0xTOkn7rKaogoSAswxK-BvA_OD_tcY",{"id":186,"title":187,"bars":7,"blurb":188,"body":189,"color":7,"contacts":7,"date":225,"dateModified":7,"description":226,"extension":84,"featured":130,"groups":7,"kicker":227,"meta":228,"metrics":7,"n":229,"navigation":85,"openTabs":7,"path":230,"progress":7,"readTime":231,"reading":7,"role":7,"rules":7,"running":7,"seo":232,"ships":7,"slug":233,"stack":7,"started":7,"status":7,"stem":234,"streak":7,"tag":235,"tagColor":237,"tagline":7,"tasks":7,"timeline":7,"topics":7,"week":7,"year":7,"__hash__":238},"en\u002Fwriting\u002F011-postgres-only.md","The weird joys of a postgres-only backend","What happens when you refuse to add a second database for five years.",{"type":10,"value":190,"toc":222},[191,194,197,201,203,219],[13,192,193],{},"№ 011 · mar 06 '26 · 6 min · stack",[17,195,187],{"id":196},"the-weird-joys-of-a-postgres-only-backend",[13,198,199],{},[23,200,188],{},[26,202],{},[13,204,205,206,210,211,214,215,218],{},"the rule is simple: if postgres can do it, postgres does it. jobs queue? ",[207,208,209],"code",{},"SELECT ... FOR UPDATE SKIP LOCKED",". full-text search? ",[207,212,213],{},"tsvector",". vector search? ",[207,216,217],{},"pg_vector",". caching? materialised views and a cron. feature flags? a table.",[13,220,221],{},"it sounds austere. it is. it's also five years of fewer moving parts, one backup story, one monitoring dashboard, one mental model. the day i have to add redis, i'll write a sad notebook about it. that day hasn't arrived.",{"title":77,"searchDepth":78,"depth":79,"links":223},[224],{"id":196,"depth":78,"text":187},"2026-03-06","The weird joys of a postgres-only backend — what happens when you refuse to add a second database for five years.","stack",{},"011","\u002Fwriting\u002F011-postgres-only","6 min",{"title":187,"description":226},"011-postgres-only","writing\u002F011-postgres-only",{"label":236,"color":237},"technical","var(--green)","ioFpcUHUP-H3rf1rNmQLtGPmDcC7-QDARw3pxQ7o0mU",{"id":240,"title":241,"bars":7,"blurb":242,"body":243,"color":7,"contacts":7,"date":266,"dateModified":7,"description":267,"extension":84,"featured":130,"groups":7,"kicker":268,"meta":269,"metrics":7,"n":270,"navigation":85,"openTabs":7,"path":271,"progress":7,"readTime":272,"reading":7,"role":7,"rules":7,"running":7,"seo":273,"ships":7,"slug":274,"stack":7,"started":7,"status":7,"stem":275,"streak":7,"tag":276,"tagColor":277,"tagline":7,"tasks":7,"timeline":7,"topics":7,"week":7,"year":7,"__hash__":278},"en\u002Fwriting\u002F010-year-limassol.md","A year of mornings in Limassol","Twelve months of 6am writing, carob-tree walks, and one broken kettle.",{"type":10,"value":244,"toc":263},[245,248,251,255,257,260],[13,246,247],{},"№ 010 · feb 22 '26 · 5 min · life",[17,249,241],{"id":250},"a-year-of-mornings-in-limassol",[13,252,253],{},[23,254,242],{},[26,256],{},[13,258,259],{},"the light here arrives early and cheap. i started waking at six, writing for an hour, walking twelve minutes to the sea before anyone else had picked up their phone.",[13,261,262],{},"twelve months later the routine is still the routine. the kettle broke in november. the carob trees are still there. the writing is mostly better. the light is still the best thing about cyprus.",{"title":77,"searchDepth":78,"depth":79,"links":264},[265],{"id":250,"depth":78,"text":241},"2026-02-22","A year of mornings in Limassol — twelve months of 6am writing, carob-tree walks, and one broken kettle.","life",{},"010","\u002Fwriting\u002F010-year-limassol","5 min",{"title":241,"description":267},"010-year-limassol","writing\u002F010-year-limassol",{"label":268,"color":277},"var(--fg-2)","El1ue11OrbqZYUPy-SMa5vPEpfIFLgXjND9D1sn4asU",{"id":280,"title":281,"bars":7,"blurb":7,"body":282,"color":7,"contacts":7,"date":7,"dateModified":7,"description":331,"extension":84,"featured":130,"groups":7,"kicker":332,"meta":333,"metrics":7,"n":7,"navigation":85,"openTabs":7,"path":334,"progress":7,"readTime":7,"reading":7,"role":7,"rules":7,"running":7,"seo":335,"ships":7,"slug":7,"stack":7,"started":7,"status":336,"stem":340,"streak":7,"tag":7,"tagColor":7,"tagline":7,"tasks":7,"timeline":7,"topics":7,"week":7,"year":7,"__hash__":341},"en\u002Fpages\u002Fwriting.md","writing",{"type":10,"value":283,"toc":328},[284,287,290,297,299,302,320,323],[13,285,286],{},"— get new broadcasts",[13,288,289],{},"one email when i publish. not more than monthly. no tracker.",[13,291,292,293],{},"rss · ",[294,295,296],"a",{"href":296},"\u002Ffeed.xml",[26,298],{},[17,300,301],{"id":301},"categories",[303,304,305,308,311,314,317],"ul",{},[45,306,307],{},"▸ case study — 4",[45,309,310],{},"▸ technical — 11",[45,312,313],{},"▸ operations — 8",[45,315,316],{},"▸ notebook — 14",[45,318,319],{},"▸ life — 5",[13,321,322],{},"archive · 2019 → now",[13,324,325],{},[23,326,327],{},"i take naps between drafts. you can too.",{"title":77,"searchDepth":78,"depth":79,"links":329},[330],{"id":301,"depth":78,"text":301},"journal, long reads, notebook. one email per new broadcast. no tracker.","▸ CH.05 — JOURNAL · LONG READS · NOTEBOOK",{},"\u002Fpages\u002Fwriting",{"title":281,"description":331},[337,338,339],"№ 014 · 42 POSTS","POSTS·42","SUBS·1.8k","pages\u002Fwriting","f4Rkx_23hR3ZlVDARSUQy4Zdk9TIz-vPikEImZGG1oU",1777071042443]