[{"data":1,"prerenderedAt":595},["ShallowReactive",2],{"home-en":3,"projects-en":4,"posts-en":351},null,[5,54,164,208],{"id":6,"title":7,"bars":8,"blurb":9,"body":10,"color":27,"contacts":3,"date":3,"dateModified":3,"description":28,"extension":29,"featured":30,"groups":3,"kicker":31,"meta":32,"metrics":33,"n":3,"navigation":43,"openTabs":3,"path":44,"progress":3,"readTime":3,"reading":3,"role":45,"rules":3,"running":3,"seo":46,"ships":3,"slug":47,"stack":3,"started":3,"status":3,"stem":48,"streak":3,"tag":49,"tagColor":3,"tagline":51,"tasks":3,"timeline":3,"topics":3,"week":3,"year":52,"__hash__":53},"en\u002Fwork\u002Fcooking.md","…something new",1,"a note-taking tool for operators of small internet companies. still an idea. writing not yet on sale.",{"type":11,"value":12,"toc":22},"minimark",[13,16,19],[14,15,9],"p",{},[14,17,18],{},"just me, for now. ~10% built, 0% public, 0 investors (thx). if you run a small internet company and keep losing track of the ten things that matter next week, drop me a line on \u002Fsignal and i'll add you to the very short beta list.",[14,20,21],{},"● draft · quiet mode",{"title":23,"searchDepth":24,"depth":25,"links":26},"",2,3,[],"var(--fg-3)","a quiet, still-cooking note-taking tool for operators of small internet companies. not on sale yet.","md",false,"▸ CH.04 \u002F cooking · quiet mode",{},[34,37,40],[35,36],"progress","~10%",[38,39],"public","no",[41,42],"investors","none, thx",true,"\u002Fwork\u002Fcooking","just me, for now",{"title":7,"description":28},"cooking","work\u002Fcooking",{"label":50,"color":27},"draft","quiet mode","2026 → ?","bE9tXsi6v8EiZpv4DLJM4nsVQkFd40kxlYXcZpbLlSM",{"id":55,"title":56,"bars":25,"blurb":57,"body":58,"color":142,"contacts":3,"date":3,"dateModified":3,"description":143,"extension":29,"featured":30,"groups":3,"kicker":144,"meta":145,"metrics":146,"n":3,"navigation":43,"openTabs":3,"path":155,"progress":3,"readTime":3,"reading":3,"role":156,"rules":3,"running":3,"seo":157,"ships":3,"slug":56,"stack":3,"started":3,"status":3,"stem":158,"streak":3,"tag":159,"tagColor":3,"tagline":161,"tasks":3,"timeline":3,"topics":3,"week":3,"year":162,"__hash__":163},"en\u002Fwork\u002Fagent-monorepo.md","agent-monorepo","a boilerplate for teams who let agents write most of the code. contracts first, humans second, agents third.",{"type":11,"value":59,"toc":137},[60,65,68,72,75,79,82,121,124,134],[61,62,64],"h2",{"id":63},"the-goal","the goal",[14,66,67],{},"make it cheap and safe to let an ai agent write most of the code in a real product — backend, web, mobile — without the parts silently drifting apart.",[61,69,71],{"id":70},"the-problem","the problem",[14,73,74],{},"agents are fast but local. they happily add a field to a nest controller and forget the nuxt call site, or rename a flutter model and leave the server schema behind. in a normal monorepo nothing stops them — types live separately per runtime, and the first real feedback is a 500 in production.",[61,76,78],{"id":77},"how-it-solves-it","how it solves it",[14,80,81],{},"one contract, three runtimes.",[83,84,85,93,99,113],"ul",{},[86,87,88,92],"li",{},[89,90,91],"strong",{},"openapi + asyncapi"," as the single source of truth for requests, events and domain models.",[86,94,95,98],{},[89,96,97],{},"codegen"," into typed clients and server stubs for nest (typescript), nuxt (typescript) and flutter (dart) — so a change in the spec breaks compile everywhere it matters, before the agent can ship it.",[86,100,101,104,105,108,109,112],{},[89,102,103],{},"prisma 7"," for the data layer, ",[89,106,107],{},"better auth"," for sessions, ",[89,110,111],{},"turbo + pnpm"," for the plumbing.",[86,114,115,116,120],{},"repo layout and readme written ",[117,118,119],"em",{},"for agents",": small, labelled surfaces, clear \"do\u002Fdon't\" boundaries, scripts agents can actually run.",[14,122,123],{},"the boilerplate itself is tiny on purpose. the idea isn't \"use this framework\" — it's \"clone this, delete what you don't need, keep the contract-first habit\".",[14,125,126,127],{},"source → ",[128,129,133],"a",{"href":130,"rel":131},"https:\u002F\u002Fgithub.com\u002Fevgentus-cy\u002Fclaude-driven-nest-nuxt-flutter-monorepo",[132],"nofollow","github.com\u002Fevgentus-cy\u002Fclaude-driven-nest-nuxt-flutter-monorepo",[14,135,136],{},"● oss · tooling · mit",{"title":23,"searchDepth":24,"depth":25,"links":138},[139,140,141],{"id":63,"depth":24,"text":64},{"id":70,"depth":24,"text":71},{"id":77,"depth":24,"text":78},"var(--green)","agent-monorepo — a spec-first, agent-friendly boilerplate stitching nestjs, nuxt and flutter around a shared openapi + asyncapi contract.","▸ CH.04 \u002F agent-monorepo · oss tooling",{},[147,150,152],[148,149],"runtimes","nest · nuxt · flutter",[151,91],"contracts",[153,154],"license","mit","\u002Fwork\u002Fagent-monorepo","author · maintainer",{"title":56,"description":143},"work\u002Fagent-monorepo",{"label":160,"color":142},"oss · tooling","spec-first monorepo for ai-coded teams","2025 → now","0Cg2Thk0QBI3xu9szEBSizdus-KjIu1Qz3S6OZuHVGI",{"id":165,"title":166,"bars":167,"blurb":168,"body":169,"color":184,"contacts":3,"date":3,"dateModified":3,"description":185,"extension":29,"featured":30,"groups":3,"kicker":186,"meta":187,"metrics":188,"n":3,"navigation":43,"openTabs":3,"path":198,"progress":3,"readTime":3,"reading":3,"role":199,"rules":3,"running":3,"seo":200,"ships":3,"slug":201,"stack":3,"started":3,"status":3,"stem":202,"streak":3,"tag":203,"tagColor":3,"tagline":205,"tasks":3,"timeline":3,"topics":3,"week":3,"year":206,"__hash__":207},"en\u002Fwork\u002Fkind.md","kind.events",5,"a calm map of what's happening this weekend. curated, no paid placements, no noise.",{"type":11,"value":170,"toc":182},[171,173,176,179],[14,172,168],{},[14,174,175],{},"built with a friend in 2024 and growing slow on purpose. 320 events a month, three cities, a waitlist of 2.4k people who want their town added.",[14,177,178],{},"currently shipping v2 — calendar view, email digests, and a cleaner map.",[14,180,181],{},"● consumer · events discovery · cyprus",{"title":23,"searchDepth":24,"depth":25,"links":183},[],"var(--amber)","kind.events — a calm, curated map of what's happening this weekend across three cyprus cities. no paid placements, no noise.","▸ CH.04 \u002F kind · recently launched",{},[189,192,195],[190,191],"events","320\u002Fmo",[193,194],"cities","3",[196,197],"waitlist","2.4k","\u002Fwork\u002Fkind","solo + 1 friend",{"title":166,"description":185},"kind","work\u002Fkind",{"label":204,"color":184},"consumer","events discovery · cyprus","2024 → now","A6qnwGYYkos0WhuI840Qku-rgOeWXifZbT8F0OQyXc8",{"id":209,"title":210,"bars":211,"blurb":212,"body":213,"color":327,"contacts":3,"date":3,"dateModified":3,"description":328,"extension":29,"featured":43,"groups":3,"kicker":329,"meta":330,"metrics":331,"n":3,"navigation":43,"openTabs":3,"path":341,"progress":3,"readTime":3,"reading":3,"role":342,"rules":3,"running":3,"seo":343,"ships":3,"slug":344,"stack":3,"started":3,"status":3,"stem":345,"streak":3,"tag":346,"tagColor":3,"tagline":348,"tasks":3,"timeline":3,"topics":3,"week":3,"year":349,"__hash__":350},"en\u002Fwork\u002Fmatchpro.md","matchpro.io",4,"a niche job-board that actually matches people, not keywords. rebuilt the matching engine this spring.",{"type":11,"value":214,"toc":321},[215,218,226,229,232,242,245,249,276,278,282,308,318],[14,216,217],{},"¶ case study — 01 · shipped spring '26 · timeline: 11 weeks · team: 1\ntags: matching · search · postgres",[61,219,221,222,225],{"id":220},"the-old-engine-matched-keywords-the-new-one-reads-the-whole-résumé","the old engine matched ",[117,223,224],{},"keywords",". the new one reads the whole résumé.",[14,227,228],{},"by evgeniy · apr 18, 2026 · ~8 min read",[14,230,231],{},"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.",[233,234,235],"blockquote",{},[14,236,237,238,241],{},"\"matching is not search. search rewards precision. matching rewards being ",[117,239,240],{},"interestingly close",".\"",[243,244],"hr",{},[61,246,248],{"id":247},"how-it-went-down","how it went down",[250,251,252,258,264,270],"ol",{},[86,253,254,257],{},[89,255,256],{},"throw away the ranker"," — kept the data, dropped the model. started from a blank ipynb.",[86,259,260,263],{},[89,261,262],{},"embed everything"," — résumé + job text → small multilingual model, cached in pg_vector.",[86,265,266,269],{},[89,267,268],{},"re-rank with signals"," — location, seniority, years-in-stack. boring features still win.",[86,271,272,275],{},[89,273,274],{},"shadow-run for 3 weeks"," — a\u002Fb on recruiters' clicks. watched dashboards more than family.",[243,277],{},[61,279,281],{"id":280},"results-11-weeks-later","results — 11 weeks later",[83,283,284,290,296,302],{},[86,285,286,289],{},[89,287,288],{},"match-quality (recruiter 👍)"," — 41% → 78% (+37 pp)",[86,291,292,295],{},[89,293,294],{},"time-to-first-click"," — 9.2s → 3.1s (−6.1s)",[86,297,298,301],{},[89,299,300],{},"weekly placements"," — 12 → 28 (×2.3)",[86,303,304,307],{},[89,305,306],{},"latency (p95)"," — 640ms → 180ms (−72%)",[309,310,315],"pre",{"className":311,"code":313,"language":314},[312],"language-text","› bench --cohort=q1-recruiters\n  running 2,140 matches…\n  ✓ v3 beats v2 on 78% of pairs\n  ! 4 regressions — investigating\n› deploy prod\n","text",[316,317,313],"code",{"__ignoreMap":23},[14,319,320],{},"● published · 41kb · 2 figures · 6 footnotes",{"title":23,"searchDepth":24,"depth":25,"links":322},[323,325,326],{"id":220,"depth":24,"text":324},"the old engine matched keywords. the new one reads the whole résumé.",{"id":247,"depth":24,"text":248},{"id":280,"depth":24,"text":281},"var(--red)","matchpro.io — a niche cyprus job-board that matches people, not keywords. rebuilt the matching engine in spring '26.","▸ CH.04 \u002F matchpro · currently shipping v3",{},[332,335,338],[333,334],"jobs","1.2k\u002Fmo",[336,337],"placements","~180",[339,340],"revenue","profitable","\u002Fwork\u002Fmatchpro","solo · design + code + ops",{"title":210,"description":328},"matchpro","work\u002Fmatchpro",{"label":347,"color":327},"featured","job-board · it · cyprus","2021 → now","W3ME2VGgLsDw_xeEtEdOafRhHRaJK22QoMt28U1AVBc",[352,418,461,503,555],{"id":353,"title":354,"bars":3,"blurb":355,"body":356,"color":3,"contacts":3,"date":405,"dateModified":3,"description":406,"extension":29,"featured":43,"groups":3,"kicker":407,"meta":408,"metrics":3,"n":409,"navigation":43,"openTabs":3,"path":410,"progress":3,"readTime":411,"reading":3,"role":3,"rules":3,"running":3,"seo":412,"ships":3,"slug":413,"stack":3,"started":3,"status":3,"stem":414,"streak":3,"tag":415,"tagColor":327,"tagline":3,"tasks":3,"timeline":3,"topics":3,"week":3,"year":3,"__hash__":417},"en\u002Fwriting\u002F014-matchpro-engine.md","Rewriting matchpro's matching engine","What I learned from watching recruiters click for three weeks.",{"type":11,"value":357,"toc":402},[358,361,364,368,370,372,378,396,399],[14,359,360],{},"▸ featured · case study — № 014 · apr 18 '26 · 8 min",[61,362,354],{"id":363},"rewriting-matchpros-matching-engine",[14,365,366],{},[117,367,355],{},[243,369],{},[14,371,231],{},[233,373,374],{},[14,375,237,376,241],{},[117,377,240],{},[250,379,380,384,388,392],{},[86,381,382,257],{},[89,383,256],{},[86,385,386,263],{},[89,387,262],{},[86,389,390,269],{},[89,391,268],{},[86,393,394,275],{},[89,395,274],{},[14,397,398],{},"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.",[14,400,401],{},"read the full postmortem on \u002Fwork\u002Fmatchpro.",{"title":23,"searchDepth":24,"depth":25,"links":403},[404],{"id":363,"depth":24,"text":354},"2026-04-18","Rewriting matchpro's matching engine — what I learned from watching recruiters click for three weeks.","case study",{},"014","\u002Fwriting\u002F014-matchpro-engine","8 min",{"title":354,"description":406},"014-matchpro-engine","writing\u002F014-matchpro-engine",{"label":416,"color":327},"long read","zKM9k3xbKEs_rywRELI7EKj4Y_-0H1SfQ1_9haVJdH8",{"id":419,"title":420,"bars":3,"blurb":421,"body":422,"color":3,"contacts":3,"date":448,"dateModified":3,"description":449,"extension":29,"featured":30,"groups":3,"kicker":450,"meta":451,"metrics":3,"n":452,"navigation":43,"openTabs":3,"path":453,"progress":3,"readTime":454,"reading":3,"role":3,"rules":3,"running":3,"seo":455,"ships":3,"slug":456,"stack":3,"started":3,"status":3,"stem":457,"streak":3,"tag":458,"tagColor":184,"tagline":3,"tasks":3,"timeline":3,"topics":3,"week":3,"year":3,"__hash__":460},"en\u002Fwriting\u002F013-support-email.md","Why I still answer every support email myself","A short defense of doing customer support on purpose.",{"type":11,"value":423,"toc":445},[424,427,430,434,436,439,442],[14,425,426],{},"№ 013 · apr 02 '26 · 4 min · operations",[61,428,420],{"id":429},"why-i-still-answer-every-support-email-myself",[14,431,432],{},[117,433,421],{},[243,435],{},[14,437,438],{},"every reply goes out under my name. not a shared inbox, not a tier-1 script, not a chatbot with a smiley avatar.",[14,440,441],{},"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.",[14,443,444],{},"the group is small on purpose. a founder's inbox is a moat, not a cost center.",{"title":23,"searchDepth":24,"depth":25,"links":446},[447],{"id":429,"depth":24,"text":420},"2026-04-02","Why I still answer every support email myself — a short defense of doing customer support on purpose.","operations",{},"013","\u002Fwriting\u002F013-support-email","4 min",{"title":420,"description":449},"013-support-email","writing\u002F013-support-email",{"label":459,"color":184},"notebook","jFeELVb3z-J36SSXjiJYoGGvuT5cWLYSqOuOKQKs_k4",{"id":462,"title":463,"bars":3,"blurb":464,"body":465,"color":3,"contacts":3,"date":491,"dateModified":3,"description":492,"extension":29,"featured":30,"groups":3,"kicker":493,"meta":494,"metrics":3,"n":495,"navigation":43,"openTabs":3,"path":496,"progress":3,"readTime":497,"reading":3,"role":3,"rules":3,"running":3,"seo":498,"ships":3,"slug":499,"stack":3,"started":3,"status":3,"stem":500,"streak":3,"tag":501,"tagColor":184,"tagline":3,"tasks":3,"timeline":3,"topics":3,"week":3,"year":3,"__hash__":502},"en\u002Fwriting\u002F012-shipping-silently.md","On shipping things when no one is watching","Slack notifications off. Keyboard on. Trust the calendar.",{"type":11,"value":466,"toc":488},[467,470,473,477,479,482,485],[14,468,469],{},"№ 012 · mar 20 '26 · 3 min · process",[61,471,463],{"id":472},"on-shipping-things-when-no-one-is-watching",[14,474,475],{},[117,476,464],{},[243,478],{},[14,480,481],{},"most weeks, nothing dramatic happens. a backend migration lands. a typo gets fixed. a small feature ships behind a flag to eleven users.",[14,483,484],{},"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.",[14,486,487],{},"calendar says ship on friday. so ship on friday.",{"title":23,"searchDepth":24,"depth":25,"links":489},[490],{"id":472,"depth":24,"text":463},"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":463,"description":492},"012-shipping-silently","writing\u002F012-shipping-silently",{"label":459,"color":184},"99k-p5IV-GKvs0xTOkn7rKaogoSAswxK-BvA_OD_tcY",{"id":504,"title":505,"bars":3,"blurb":506,"body":507,"color":3,"contacts":3,"date":542,"dateModified":3,"description":543,"extension":29,"featured":30,"groups":3,"kicker":544,"meta":545,"metrics":3,"n":546,"navigation":43,"openTabs":3,"path":547,"progress":3,"readTime":548,"reading":3,"role":3,"rules":3,"running":3,"seo":549,"ships":3,"slug":550,"stack":3,"started":3,"status":3,"stem":551,"streak":3,"tag":552,"tagColor":142,"tagline":3,"tasks":3,"timeline":3,"topics":3,"week":3,"year":3,"__hash__":554},"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":11,"value":508,"toc":539},[509,512,515,519,521,536],[14,510,511],{},"№ 011 · mar 06 '26 · 6 min · stack",[61,513,505],{"id":514},"the-weird-joys-of-a-postgres-only-backend",[14,516,517],{},[117,518,506],{},[243,520],{},[14,522,523,524,527,528,531,532,535],{},"the rule is simple: if postgres can do it, postgres does it. jobs queue? ",[316,525,526],{},"SELECT ... FOR UPDATE SKIP LOCKED",". full-text search? ",[316,529,530],{},"tsvector",". vector search? ",[316,533,534],{},"pg_vector",". caching? materialised views and a cron. feature flags? a table.",[14,537,538],{},"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":23,"searchDepth":24,"depth":25,"links":540},[541],{"id":514,"depth":24,"text":505},"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":505,"description":543},"011-postgres-only","writing\u002F011-postgres-only",{"label":553,"color":142},"technical","ioFpcUHUP-H3rf1rNmQLtGPmDcC7-QDARw3pxQ7o0mU",{"id":556,"title":557,"bars":3,"blurb":558,"body":559,"color":3,"contacts":3,"date":582,"dateModified":3,"description":583,"extension":29,"featured":30,"groups":3,"kicker":584,"meta":585,"metrics":3,"n":586,"navigation":43,"openTabs":3,"path":587,"progress":3,"readTime":588,"reading":3,"role":3,"rules":3,"running":3,"seo":589,"ships":3,"slug":590,"stack":3,"started":3,"status":3,"stem":591,"streak":3,"tag":592,"tagColor":593,"tagline":3,"tasks":3,"timeline":3,"topics":3,"week":3,"year":3,"__hash__":594},"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":11,"value":560,"toc":579},[561,564,567,571,573,576],[14,562,563],{},"№ 010 · feb 22 '26 · 5 min · life",[61,565,557],{"id":566},"a-year-of-mornings-in-limassol",[14,568,569],{},[117,570,558],{},[243,572],{},[14,574,575],{},"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.",[14,577,578],{},"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":23,"searchDepth":24,"depth":25,"links":580},[581],{"id":566,"depth":24,"text":557},"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":557,"description":583},"010-year-limassol","writing\u002F010-year-limassol",{"label":584,"color":593},"var(--fg-2)","El1ue11OrbqZYUPy-SMa5vPEpfIFLgXjND9D1sn4asU",1777071041961]