<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts | Ben Sadik</title><description>Posts by Ben Sadik in English.</description><link>https://bensadik.net/posts/</link><language>en</language><atom:link href="https://bensadik.net/posts/rss" rel="self" type="application/rss+xml"/><item><title>Software Factories and the Future of Consulting</title><link>https://bensadik.net/posts/software-factories-and-the-future-of-consulting/</link><guid isPermaLink="true">https://bensadik.net/posts/software-factories-and-the-future-of-consulting/</guid><description>When AI agents can maintain legacy code, consulting firms that sell hours lose their edge. What they can still sell is experience and confidence.</description><pubDate>Sun, 01 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;The Astro team reported in a &lt;a href=&quot;https://blog.cloudflare.com/astro-issue-triage/&quot;&gt;blog post&lt;/a&gt; that a pipeline of AI agents had cut the repo’s open GitHub issues from more than 200 to about 30. For each bug, the pipeline reproduces it in a sandbox, diagnoses the root cause, and ships a preview release that the original reporter can verify. Astro calls this pipeline a &lt;strong&gt;software factory&lt;/strong&gt;, a concept that many software companies are now trying to build, and has open-sourced the &lt;a href=&quot;https://flueframework.com/&quot;&gt;pipeline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea is that a production bug already moves through a loop with clear inputs and outputs. People copy context from alert to Slack to ticket to branch to PR. A software factory for bug fixes runs that loop as a system of agents.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bensadik.net/images/posts/software-factories-and-the-future-of-consulting.webp&quot; class=&quot;image-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-pswp-width=&quot;1600&quot; data-pswp-height=&quot;593&quot;&gt;&lt;img src=&quot;https://bensadik.net/images/posts/software-factories-and-the-future-of-consulting.webp&quot; alt=&quot;The same bug loop, by hand and as a software factory: people carry the context from alert to Slack to ticket to branch to PR, while in the factory agents reproduce, diagnose, fix and ship a preview release before a human gate approves the PR.&quot; loading=&quot;lazy&quot; class=&quot;theme-light&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This innovation matters most for consulting firms whose offering rests largely on their clients’ legacy systems. These firms do the complex work of reading and maintaining that code, triaging issues to separate bugs from features, and modernizing the systems without breaking anything.&lt;/p&gt;
&lt;p&gt;But what happens when clients can do this work in-house, more easily and cheaply, without hiring expensive transcultural contractors? I expect most clients to bring the work in-house, and these firms to lose more clients every year as those clients learn to leverage AI.&lt;/p&gt;
&lt;p&gt;These firms can shift their offering from “we have many cheap engineers who can read your legacy code and grind through tickets” to designing and setting up software factories. Each software factory fits a client’s own budget, criticality, and compliance constraints.&lt;/p&gt;
&lt;p&gt;You may ask why clients would not build software factories in-house too. I believe these firms really sell two things. The first is experience: they have maintained legacy systems for many clients, so they know where a factory is likely to break on a legacy codebase. The second is confidence that the software factories work as intended, that the right human verification gates are in place, and that nothing triggers a regulatory issue. Because LLMs are non-deterministic, that confidence matters even more. After all, consulting firms have long sold expertise and risk transfer as well as hours.&lt;/p&gt;
&lt;p&gt;Firms that bill hours for ticket triage, or whose edge is labor-cost arbitrage, are the most exposed. Firms that productize a factory, price it on outcomes and put their name behind it are the best placed. This holds most in regulated industries, where a client’s own compliance team will likely want an outside party to share the risk.&lt;/p&gt;
&lt;p&gt;Astro is close to a best case, with strong tests, reporters who send reproductions and maintainers who know the code. Its team says that when the pipeline fails, the cause is usually opaque abstractions, missing comments or thin test coverage. This is one open-source project reporting its own results, so whether the approach works for a regulated bank is still unproven.&lt;/p&gt;
&lt;p&gt;Clients that build their own factories will still face one question when something goes wrong: who vouches for this? Consulting firms that can answer it, with experience, gates, audit trails and a contract behind them, will have a business. Those that cannot will be competing with a free GitHub Action.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>tech</category><category>opinions</category></item><item><title>I rethought my Neovim config</title><link>https://bensadik.net/posts/i-rethought-my-neovim-config/</link><guid isPermaLink="true">https://bensadik.net/posts/i-rethought-my-neovim-config/</guid><description>Agents write most of my code now, so I stripped Neovim down to reading and small edits and moved everything else to dedicated tools.</description><pubDate>Sun, 25 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;Agents write most of my code now. So I stripped my &lt;a href=&quot;https://neovim.io/&quot;&gt;Neovim&lt;/a&gt; config down to almost nothing.&lt;/p&gt;
&lt;p&gt;Gone: &lt;a href=&quot;https://www.lazyvim.org/&quot;&gt;LazyVim&lt;/a&gt;, &lt;a href=&quot;https://neovim.io/doc/user/lsp.html&quot;&gt;LSP&lt;/a&gt;, completion, pickers, file explorer.&lt;/p&gt;
&lt;p&gt;Left: my keymaps, &lt;a href=&quot;https://github.com/nvim-treesitter/nvim-treesitter&quot;&gt;treesitter&lt;/a&gt; highlighting, format on save.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bensadik.net/images/posts/i-rethought-my-neovim-config.webp&quot; class=&quot;image-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-pswp-width=&quot;1600&quot; data-pswp-height=&quot;943&quot;&gt;&lt;img src=&quot;https://bensadik.net/images/posts/i-rethought-my-neovim-config.webp&quot; alt=&quot;lazy-lock.json diff, +2 −61&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Neovim is just for reading and small edits now. Reading a diff or fixing one line doesn’t need a language server.&lt;/p&gt;
&lt;p&gt;Everything else moved up a level. I use &lt;a href=&quot;https://github.com/Gaurav-Gosain/tuios&quot;&gt;tuios&lt;/a&gt;, and I set up keybinds at its level to pop up a cli tool whenever I want to do something manually:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/alexpasmantier/television&quot;&gt;Television&lt;/a&gt; for fuzzy searching&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sxyazi/yazi&quot;&gt;Yazi&lt;/a&gt; for browsing the file system&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jesseduffield/lazygit&quot;&gt;LazyGit&lt;/a&gt; for… you know what lazygit is. If you don’t, what are you waiting for?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yassinebridi/serpl&quot;&gt;Serpl&lt;/a&gt; for search and replace (made by a Moroccan btw)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of them know or care what my editor is.&lt;/p&gt;
&lt;p&gt;Now that my Neovim is stripped away and I use a dedicated tool for each job, I feel more compliant with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Unix_philosophy&quot;&gt;unix philosophy&lt;/a&gt; than ever. Each tool does one thing, Neovim included.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>tech</category></item><item><title>Just one more prompt</title><link>https://bensadik.net/posts/just-one-more-prompt/</link><guid isPermaLink="true">https://bensadik.net/posts/just-one-more-prompt/</guid><description>Coding agents made building fast and effortless, and that’s exactly what makes them so hard to put down.</description><pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;I have an idea, I write a prompt, and half a minute later there’s something on my screen that actually runs. Sometimes it’s exactly what I wanted, sometimes it’s completely broken, and I never know which one I’ll get until I look. So I look, and then I write another prompt, and then another one.&lt;/p&gt;
&lt;p&gt;Programming used to slow me down on its own. There were errors to fix, docs to read, builds and tests to wait for. At some point I’d hit a wall, get up, make a coffee, and come back later with a clearer head. It was frustrating at the time, but looking back, those walls were what made me stop. Coding agents took them away, and nothing replaced them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A tool that removes all the friction from starting also removes all the friction from not stopping.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I never sit down in the evening and decide to code until 3 AM. What actually happens is that I decide, over and over, to keep going for a few more minutes. One more prompt before dinner, one more before bed. Each of those decisions feels reasonable on its own, and it’s only when I add them up that I realize where the night went.&lt;/p&gt;
&lt;p&gt;It doesn’t help that the finish line keeps moving. A feature looks almost done, so it feels like one more prompt will close it. It rarely does. The last part usually takes more back-and-forth than everything before it, but because I feel so close, I keep going. And when I finally go to bed, my brain doesn’t stop. It keeps writing prompts in the dark: a bug I could fix in five minutes, a small tool I could build before breakfast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It feels like a slot machine because it works like one.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sciencedirect.com/science/article/pii/S0306460323000217&quot;&gt;Research on behavioral addictions&lt;/a&gt; points to two things that make an activity addictive even without any substance involved: rewards you can’t predict, and a loop you can repeat very quickly. Slot machines have both, and so does social media. Coding agents have both too, but the reward is better than a like or a number going up. It’s something I built, and that’s much harder to walk away from.&lt;/p&gt;
&lt;p&gt;That’s also what makes it so easy to excuse. I’m not doomscrolling, I’m making things, and some of them are projects I had been putting off for years. But being productive doesn’t make it healthy. A night of lost sleep is the same whether I spent it on TikTok or on a side project nobody asked for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The real curse is how little effort it takes.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When building something only costs a prompt, I stop asking whether it’s worth building at all. Effort used to work as a filter: if an idea wasn’t worth a weekend of my time, it stayed in my notes and quietly died there. Now it costs thirty seconds, so it gets built. The first hour of a new project is pure reward, because everything works and nothing is broken yet. Then the edge cases show up, along with the docs, the cleanup, and all the boring work that makes something actually useful, and the rewards dry up. That’s usually when I open a new tab and start something else.&lt;/p&gt;
&lt;p&gt;So I end up busy without really getting anywhere, with a lot of output and very little of it that matters. And because it came so easily, it doesn’t stick. I understand and remember the things I struggled to build. The things I prompted into existence, I barely remember making.&lt;/p&gt;
&lt;p&gt;The friction developers spent years trying to remove was doing a job nobody noticed. Waiting for a build or getting stuck on a bug forced a pause. The agent never needs one. It never tells me to pick this up tomorrow. It just waits for the next prompt.&lt;/p&gt;
&lt;p&gt;So the pauses have to come from me. That means closing the agent an hour before bed, writing late-night ideas in a notebook instead of opening a terminal, and keeping a list of new ideas rather than building each one the moment it shows up. It also means counting hours instead of features, because shipping six features between midnight and 4 AM is not a good day, no matter how productive it feels.&lt;/p&gt;
&lt;p&gt;One comment in this &lt;a href=&quot;https://www.reddit.com/r/ClaudeCode/comments/1r6y419/is_claude_code_addictive/&quot;&gt;Reddit thread&lt;/a&gt; put it better than I can: “AI saves you time. It doesn’t mean you have to fill that time with more work. Fill it with thinking about what you should do and why.”&lt;/p&gt;&lt;/div&gt;</content:encoded><category>health</category><category>tech</category></item><item><title>Nix makes more sense in the age of AI</title><link>https://bensadik.net/posts/nix-makes-more-sense-in-the-age-of-ai/</link><guid isPermaLink="true">https://bensadik.net/posts/nix-makes-more-sense-in-the-age-of-ai/</guid><description>How AI coding agents pushed me toward plain text, git, and file-first tools, and why they finally make Nix worth its learning curve.</description><pubDate>Sun, 12 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://bensadik.net/images/posts/nix-makes-more-sense-in-the-age-of-ai.webp&quot; class=&quot;image-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-pswp-width=&quot;1600&quot; data-pswp-height=&quot;1006&quot;&gt;&lt;img src=&quot;https://bensadik.net/images/posts/nix-makes-more-sense-in-the-age-of-ai.webp&quot; alt=&quot;My dotfiles repository open in the terminal&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Two years ago, I went pretty deep into &lt;a href=&quot;https://nixos.org/&quot;&gt;Nix&lt;/a&gt; and &lt;a href=&quot;http://nix-darwin.org&quot;&gt;nix-darwin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I spent a lot of time setting up my MacBook config so it would be declarative, reproducible, tracked in git, and more reliable than a bunch of manual changes and forgotten steps. I wanted a setup I could trust, rebuild easily, and roll back without stress.&lt;/p&gt;
&lt;p&gt;I talked about it with colleagues, and a lot of them liked the idea immediately. The benefits were easy to explain. Reproducibility makes sense. Versioning makes sense. Treating your machine config like code makes sense.&lt;/p&gt;
&lt;p&gt;A few of them even tried it seriously.&lt;/p&gt;
&lt;p&gt;Still, most people bounced off it. The learning curve was steep, and the amount of effort it took up front was hard to justify. I can hardly blame them. Nix asks for a lot before it starts giving much back. You have to model your system, learn a new way of thinking, and fight through a lot of confusing edges. Sometimes you spend an absurd amount of time on a change that would have taken five minutes in a settings panel.&lt;/p&gt;
&lt;p&gt;So even though I believed in the idea, I always felt a bit hesitant when recommending it. The upside was real. The cost was real too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Two years later, I think the tradeoff looks better.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nix itself is still Nix. It has not suddenly become friendly. The initial setup still takes real time and real patience. What changed is everything around it.&lt;/p&gt;
&lt;p&gt;AI coding agents are now good enough to be genuinely useful on codebases like this. Tools like Codex, Claude Code, OpenCode, and Cursor can read through a Nix repo, follow the structure, understand patterns, and make the kinds of edits that used to require me to reload a lot of context into my head. I still want to review the result, obviously. But the routine burden is lower than it used to be.&lt;/p&gt;
&lt;p&gt;That changes things more than I expected.&lt;/p&gt;
&lt;p&gt;A big part of the cost of Nix was never just learning it once. The real cost was having to keep paying that tax every time you wanted to tweak something. Every small change meant re-entering the mental model, re-learning some corner of the language, re-discovering how a particular option worked.&lt;/p&gt;
&lt;p&gt;That loop is much less painful now.&lt;/p&gt;
&lt;p&gt;If the structure is already there, and the repo is reasonably clean, an agent can do a lot of the translation work between “I want this change” and “this is how the change should be expressed in Nix.” That makes the initial investment easier to defend. You are still paying for a system with a steep slope. You are just no longer climbing every part of it alone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And honestly, this goes beyond Nix.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lately I keep coming back to the idea that more of the things I care about should live as plain files in a repository.&lt;/p&gt;
&lt;p&gt;My blog as Markdown files.&lt;br&gt;
My machine config as Nix packages.&lt;br&gt;
My finances as &lt;code&gt;hledger&lt;/code&gt; journals.&lt;br&gt;
Scripts, notes, little tools, all of it.&lt;/p&gt;
&lt;p&gt;AI tools are unusually good at working with that kind of material. Give them plain text files and code, and they can actually do useful work: explain them, clean them up, refactor them, spot duplication, suggest better defaults, flag suspicious patterns, and automate the boring parts.&lt;/p&gt;
&lt;p&gt;And the nice thing is that this does not require giving up ownership.&lt;/p&gt;
&lt;p&gt;Quite the opposite. The more my stuff lives in simple files, the more control I keep. I can store it wherever I want. I can sync it however I want. I can inspect it with normal tools. I can diff it. I can revert it. I can roll back to an earlier state with a couple of git commands and move on with my life.&lt;/p&gt;
&lt;p&gt;There is a real comfort in that.&lt;/p&gt;
&lt;p&gt;It is not flashy comfort. It is the comfort of knowing that the important parts are legible and portable. They are not trapped in some product that feels convenient right up until the moment you want to leave, audit something, or recover from a mistake.&lt;/p&gt;
&lt;p&gt;Git gets more valuable in that setup too. It stops being just version control and starts becoming memory, audit trail, and safety net. If an agent makes a weird change, I can inspect the diff. If I take things in the wrong direction, I can go back. If I want to understand how a system evolved, the history is sitting there in plain sight.&lt;/p&gt;
&lt;p&gt;That is one of the more interesting consequences of the current AI moment, at least for me.&lt;/p&gt;
&lt;p&gt;A lot of people seem to take AI as a reason to move further into opaque tools and magical interfaces. I keep landing in the opposite place. The better these agents get, the more I want my own work represented as files instead of hidden application state.&lt;/p&gt;
&lt;p&gt;Once your workflows are in a repo, the agent becomes much more useful.&lt;/p&gt;
&lt;p&gt;I still would not trust a model blindly with config, finance data, or anything else where small mistakes can have annoying consequences.&lt;/p&gt;
&lt;p&gt;But the maintenance story really has changed.&lt;/p&gt;
&lt;p&gt;A few years ago, adopting something like Nix often meant accepting that you personally would carry the full cognitive load forever. Now it feels more like front-loading the modeling work, then letting an agent handle a good chunk of the maintenance.&lt;/p&gt;
&lt;p&gt;That is why Nix makes more sense to me today than it did two years ago.&lt;/p&gt;
&lt;p&gt;The complexity is still there. What changed is the amount of leverage you get in return.&lt;/p&gt;
&lt;p&gt;And that same logic is pushing me toward a broader way of working: code-first when it makes sense, file-first almost everywhere else. In a world full of capable coding agents, owning your workflows as text files in a repo feels less like a niche preference and more like solid personal infrastructure.&lt;/p&gt;
&lt;p&gt;The old pitch for Nix was reproducibility.&lt;/p&gt;
&lt;p&gt;Now I think the pitch is reproducibility, plus the ability to delegate a lot of the annoying work without giving up control.&lt;/p&gt;
&lt;p&gt;My personal config is open source, &lt;a href=&quot;https://codeberg.org/bensadik/dotfiles&quot;&gt;check it here&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>tech</category></item><item><title>RFC: A convention for CLI tools to output LLM-optimized results</title><link>https://bensadik.net/posts/llm-output-convention/</link><guid isPermaLink="true">https://bensadik.net/posts/llm-output-convention/</guid><description>AI agents run CLI tools all day, but the output is made for humans. A simple --llm flag could fix that.</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;AI coding agents like Claude Code, Codex, and OpenCode run CLI tools all the time: tests, linters, builds. But these tools talk to humans. They print colors, progress bars, banners, long stack traces, and a line for every test that passed.&lt;/p&gt;
&lt;p&gt;The agent pays for every one of those tokens, and the noise makes it worse at its job. It only needs to know what failed and why.&lt;/p&gt;
&lt;p&gt;So I wrote a small proposal: a &lt;code&gt;--llm&lt;/code&gt; flag that tells a tool “an LLM is reading this, keep it short.”&lt;/p&gt;
&lt;h2 id=&quot;why-not---json-or---quiet&quot;&gt;Why not &lt;code&gt;--json&lt;/code&gt; or &lt;code&gt;--quiet&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--json&lt;/code&gt; is made for scripts. It includes everything, and it’s often 10 to 50 times bigger than a short text summary. LLMs read text from top to bottom; they don’t walk through keys.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--quiet&lt;/code&gt; hides too much. The agent learns that something failed, but not what or why.&lt;/p&gt;
&lt;p&gt;What’s missing is something in between: short, plain text, and still enough to fix the problem.&lt;/p&gt;
&lt;h2 id=&quot;the-idea&quot;&gt;The idea&lt;/h2&gt;
&lt;p&gt;Two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;--llm&lt;/code&gt; flag: &lt;code&gt;jest --llm&lt;/code&gt;, &lt;code&gt;eslint --llm&lt;/code&gt;, &lt;code&gt;cargo build --llm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;LLM_OUTPUT=1&lt;/code&gt; environment variable, so an agent can turn it on once for every command it runs. A flag passed explicitly, like &lt;code&gt;--json&lt;/code&gt;, still wins.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When it’s on, a tool should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drop decoration: colors, spinners, box drawings, banners.&lt;/li&gt;
&lt;li&gt;Report failures, not successes, plus one summary line like &lt;code&gt;FAIL 2/50&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Keep stack traces short: the error and the top frames from your own code. No &lt;code&gt;node_modules&lt;/code&gt;, no code previews with arrows.&lt;/li&gt;
&lt;li&gt;Use a flat format that reads top to bottom.&lt;/li&gt;
&lt;li&gt;Still give enough detail to fix the problem. &lt;code&gt;--llm&lt;/code&gt; is not &lt;code&gt;--quiet&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The test is simple: could a good developer fix the bug with only this output and the codebase?&lt;/p&gt;
&lt;h2 id=&quot;an-example&quot;&gt;An example&lt;/h2&gt;
&lt;p&gt;A failing Jest run normally prints dozens of lines: PASS lines, code previews, arrows, full stack traces, and a summary table. With &lt;code&gt;--llm&lt;/code&gt;, it becomes:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FAIL 2/50&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;--- src/auth.test.ts:42 &quot;should reject expired tokens&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;expected: 401&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;received: 200&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;--- src/auth.test.ts:67 &quot;should refresh token on 403&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TypeError: refreshToken is not a function&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  at handleResponse (src/api.ts:15)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything the agent needs, for a fraction of the tokens. If it wants the code around line 42, it can read the file itself.&lt;/p&gt;
&lt;h2 id=&quot;how-it-could-spread&quot;&gt;How it could spread&lt;/h2&gt;
&lt;p&gt;No standards body, no committee. Like &lt;code&gt;--verbose&lt;/code&gt; and &lt;code&gt;--quiet&lt;/code&gt;, it only works if people use it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build it into the tools agents run the most: Jest, ESLint, pytest.&lt;/li&gt;
&lt;li&gt;Send PRs upstream, or ship it as plugins.&lt;/li&gt;
&lt;li&gt;Have agent harnesses set &lt;code&gt;LLM_OUTPUT=1&lt;/code&gt; by default.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Later, the flag could take values like &lt;code&gt;--llm=verbose&lt;/code&gt;. For now, it’s a simple on/off.&lt;/p&gt;
&lt;p&gt;The full proposal, with the details and an FAQ, is on &lt;a href=&quot;https://codeberg.org/bensadik/llm-output-convention&quot;&gt;Codeberg&lt;/a&gt;. Ideas and contributions are welcome.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>tech</category></item><item><title>May I Smoke Here?</title><link>https://bensadik.net/posts/may-i-smoke-here/</link><guid isPermaLink="true">https://bensadik.net/posts/may-i-smoke-here/</guid><description>If you need to ask whether it&apos;s okay to smoke here, you already know the answer.</description><pubDate>Sat, 21 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;Someone has probably asked you this before. Or maybe you’re a smoker and you’ve asked it yourself, standing in the middle of a group of friends, lighter already halfway out of your pocket. It sounds polite. It sounds considerate. But I’d argue it’s neither.&lt;/p&gt;
&lt;p&gt;This is written to you, the smoker. Not to lecture, not to guilt-trip, but because I think this small question deserves more honesty than it usually gets.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bensadik.net/images/posts/may-i-smoke-here.webp&quot; class=&quot;image-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-pswp-width=&quot;735&quot; data-pswp-height=&quot;548&quot;&gt;&lt;img src=&quot;https://bensadik.net/images/posts/may-i-smoke-here.webp&quot; alt=&quot;Cowboy Bebop, Spike Spiegel and Faye Valentine smoking&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Asking “May I smoke here?” is not courtesy. It’s a trap dressed up as manners.&lt;/p&gt;
&lt;p&gt;Think about what happens when you ask. The answer is almost always “Yeah, sure” or “I don’t mind.” And why wouldn’t it be? Nobody wants to be the person who says no. Nobody wants to come off as difficult, or controlling, or dramatic about it. So people say yes, not because they mean it, but because they feel they have to. That “yes” comes from politeness, not permission. It comes from someone who doesn’t feel entitled to say what they actually think, which is probably something closer to: &lt;em&gt;I’d really rather you didn’t.&lt;/em&gt; You should never take that answer at face value.&lt;/p&gt;
&lt;p&gt;And let’s say they do mean it. Let’s say the person across from you genuinely doesn’t care. That still doesn’t make it okay. You know what cigarette smoke does. You’ve read the warnings, you’ve seen the campaigns. Secondhand smoke is not some abstract concern. It’s a real, documented health hazard, and lighting up next to someone means actively choosing to expose them to it. The same goes for vaping, by the way. It’s newer, the long-term effects are still unclear, and that uncertainty is not a free pass. If anything, not knowing what it does to the people around you is a stronger reason to keep it away from them.&lt;/p&gt;
&lt;p&gt;If someone walks up to you while you’re already smoking, the courteous move isn’t to keep going. It’s to put it out, talk to them, and light up again later when you’re alone or around fellow smokers. That small gesture says more about your character than any polite question ever could.&lt;/p&gt;
&lt;p&gt;You probably already know smoking is destroying your body. You’ve probably tried to quit, maybe more than once. Addiction is real, and I’m not here to pretend it’s just a matter of willpower. It’s a trap, and escaping it is genuinely difficult. But if you haven’t found a way to stop hurting yourself yet, at the very least, stop making it someone else’s problem.&lt;/p&gt;
&lt;p&gt;And honestly, the people who ask are already a step ahead of the ones who don’t. You know the type. Lighting up in a closed room. Smoking right next to someone without a second thought. But even in places where it’s technically allowed, where ashtrays sit on every table and the air is already heavy, you don’t have to add to it. Self-awareness doesn’t take a day off just because the venue permits it.&lt;/p&gt;
&lt;p&gt;So here’s the alternative, and it’s simple. Step away. Go to a terrace, a balcony, an empty corner somewhere. Smoke where you’re not making it anyone else’s business. You’ll be more comfortable, nobody has to perform their “I don’t mind” routine, and the whole awkward permission ritual disappears.&lt;/p&gt;
&lt;p&gt;Never ask “May I smoke here?” Not because you don’t care, but because you care enough not to put anyone in that position.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>opinions</category><category>health</category></item><item><title>Zod validation that reads like docs</title><link>https://bensadik.net/posts/zod-validation-that-reads-like-docs/</link><guid isPermaLink="true">https://bensadik.net/posts/zod-validation-that-reads-like-docs/</guid><description>Add runtime validation to TypeScript class methods with Zod decorators.</description><pubDate>Mon, 06 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;TypeScript types disappear at runtime. If you work with classes in Angular, NestJS, or any other TypeScript codebase that leans on OOP, that can leave an important gap at method boundaries.&lt;/p&gt;
&lt;p&gt;This pattern uses decorators and Zod to validate method inputs and return values at runtime while keeping the TypeScript types aligned.&lt;/p&gt;
&lt;h2 id=&quot;zoddoc&quot;&gt;ZodDoc&lt;/h2&gt;
&lt;p&gt;ZodDoc is a small set of &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/decorators.html&quot;&gt;TypeScript decorators&lt;/a&gt; that use &lt;a href=&quot;https://zod.dev/?id=introduction&quot;&gt;Zod schemas&lt;/a&gt; to validate method parameters and return values. It looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Statistics&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;    // Returns the average of two numbers.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;param&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, number) &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// `x` - The first input number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;param&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, number) &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// `y` - The second input number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(number) &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// The arithmetic mean of `x` and `y`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; getAverage&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; (x &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; y) &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; 2.0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;number&lt;/code&gt; argument passed to &lt;code&gt;@param&lt;/code&gt; and &lt;code&gt;@returns&lt;/code&gt; is the Zod schema itself. The omitted import looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { number } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;zod&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;
&lt;h3 id=&quot;zod&quot;&gt;Zod&lt;/h3&gt;
&lt;p&gt;Install Zod with &lt;code&gt;npm i zod&lt;/code&gt;. If you are not already using it, the &lt;a href=&quot;https://zod.dev/?id=introduction&quot;&gt;official documentation&lt;/a&gt; covers the schema API in detail.&lt;/p&gt;
&lt;h3 id=&quot;typescript-decorators&quot;&gt;TypeScript Decorators&lt;/h3&gt;
&lt;p&gt;Angular and NestJS already rely on decorators, so this usually works out of the box there. In other setups, you may need to enable decorators in &lt;code&gt;tsconfig.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;  &quot;compilerOptions&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;    &quot;target&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;ES5&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;    &quot;experimentalDecorators&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;implementing-returns&quot;&gt;Implementing &lt;code&gt;returns&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Start with a simple &lt;code&gt;returns&lt;/code&gt; decorator:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { z } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;zod&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;Schema&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;        _target&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Object&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;        _propertyKey&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; symbol&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;		descriptor&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; TypedPropertyDescriptor&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;(&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;[]) &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;            // TODO: Implement logic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a standard method decorator factory. The generic &lt;code&gt;T&lt;/code&gt; ties the Zod schema type &lt;code&gt;z.Schema&amp;#x3C;T&gt;&lt;/code&gt; to the decorated method type &lt;code&gt;(...args: any[]) =&gt; T&lt;/code&gt;, so TypeScript can catch mismatches at compile time:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(z.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;()) &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// 🔴 Argument of type&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                     // &apos;TypedPropertyDescriptor&amp;#x3C;() =&gt; string&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                     // is not assignable to parameter of type&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                     // &apos;TypedPropertyDescriptor&amp;#x3C;(...args: any[]) =&gt; number&gt;&apos;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;public &lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;doSomething&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(): string {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now implement the runtime check:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { z } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;zod&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;Schema&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;        _target&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Object&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;        _propertyKey&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; symbol&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;		descriptor&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; TypedPropertyDescriptor&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;(&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;[]) &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;descriptor &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;descriptor.value) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;			    return&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;		    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;		    const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; originalMethod&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; descriptor.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;            &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;            // replace the original method with our own implementation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;		    descriptor.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; unknown&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;[])&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                // Grab the original result&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;			    const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; originalResult&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; originalMethod?.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;apply&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, args);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                // Call the `parse` method of the supplied schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                // with the original result as parameter.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                // If the original result is conforming to the schema, it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;                // will be returned. Otherwise, a `ZodError` will be thrown.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;			    return&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; schema.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(originalResult);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;		    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;		    return&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; descriptor;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The decorator replaces the original method, calls it, and validates the returned value with &lt;code&gt;schema.parse&lt;/code&gt;. If the value does not match, Zod throws a &lt;code&gt;ZodError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can then use it like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { z } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;zod&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { returns } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;...&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Statistics&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(z.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; getAverage&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; (x &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; y) &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; 2.0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also accept a function that returns a schema. That makes primitives slightly nicer to write, for example &lt;code&gt;@returns(number)&lt;/code&gt; instead of &lt;code&gt;@returns(z.number())&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;Schema&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;schema&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;Schema&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt; &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;Schema&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;     // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;       if&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; (schema &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Function&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;           return&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; schema&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(originalResult);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;       }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; schema.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(originalResult);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;     // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This also works with custom schemas:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/.../UserResponse.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { z } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;zod&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// Define the schema once.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; UserResponse&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; z.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;	id: z.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;uuid&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;	name: z.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;min&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;	sex: z.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;enum&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;male&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;female&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;	birthdate: z.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;date&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// Infer the TypeScript type from the schema.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; UserResponse&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; UserResponse&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;src/.../UserService.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { UserResponse } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;./UserResponse&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { promise } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;zod&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { returns } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;...&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; { httpClient } &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; &quot;...&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; UserService&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;promise&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(UserResponse))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; getAuthUser&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;UserResponse&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; httpClient.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;UserResponse&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;&quot;auth/me&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same approach can be used to implement &lt;code&gt;@param&lt;/code&gt; as well: capture the original method, validate the targeted argument before calling it, and then delegate to the original implementation.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>tech</category></item><item><title>My participation in the ENSET Mohammedia&apos;s GEIW Hackathon</title><link>https://bensadik.net/posts/my-participation-in-the-enset-mohammedia-geiw-hackathon/</link><guid isPermaLink="true">https://bensadik.net/posts/my-participation-in-the-enset-mohammedia-geiw-hackathon/</guid><description>Our team TL3B took second prize at ENSET Mohammedia’s GEIW hackathon with a healthcare backend built on blockchain and AI.</description><pubDate>Mon, 20 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;div dir=&quot;ltr&quot; lang=&quot;en&quot;&gt;&lt;p&gt;From March 15 to 17, ENSET Mohammedia held its Global Entrepreneurship and Innovation Week (GEIW). Part of it was a hackathon with one theme: use blockchain and AI to improve healthcare.&lt;/p&gt;
&lt;p&gt;I joined with four friends from second-year GLSID and II-BDCC. We called the team TL3B, after the initials of our last names: Achraf &lt;strong&gt;T&lt;/strong&gt;affah, Abderrazzaq &lt;strong&gt;L&lt;/strong&gt;aanaoui, Ahmed El &lt;strong&gt;B&lt;/strong&gt;ouchouki, Mohamed &lt;strong&gt;B&lt;/strong&gt;enchelbikh, and me, &lt;strong&gt;B&lt;/strong&gt;en Sadik.&lt;/p&gt;
&lt;p&gt;Our idea was a platform that connects patients and doctors. Medical records would be stored on a blockchain, so patients stay in control of who can see their data. On top of that, an AI service would suggest a diagnosis based on a patient’s symptoms and medical history.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1684886263139/747c0186-f824-49b1-b06c-8bd1e062d46f.jpeg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1684886263139/747c0186-f824-49b1-b06c-8bd1e062d46f.jpeg&quot; alt=&quot;TL3B architecture: a gateway routing HTTP calls to NestJS, .NET, Python, and Node.js microservices, with PostgreSQL, IPFS, and a blockchain ledger&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We built it as microservices talking to each other over REST. That choice had more to do with us than with the problem. I’m fastest with .NET, Ahmed and Abderrazzaq are more at home with NestJS, and the AI part made more sense in Python. Splitting the backend let each of us work in the stack we knew best.&lt;/p&gt;
&lt;p&gt;We ran out of time before we could build a front-end, so during the defence we showed what we had: the backend. The code is unfinished and rough, but it’s still up on the &lt;a href=&quot;https://github.com/orgs/TL3B-HACKATHON/repositories&quot;&gt;GitHub organization&lt;/a&gt; we made for the hackathon.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1684886355846/54480d21-0c83-49a8-9cb9-4dcce11c0a0f.jpeg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1684886355846/54480d21-0c83-49a8-9cb9-4dcce11c0a0f.jpeg&quot; alt=&quot;Team TL3B in hackathon t-shirts presenting the project during the GEIW hackathon defence&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We took second prize, and I was proud of what we pulled off together.&lt;/p&gt;
&lt;p&gt;I got to try blockchain and AI on a real project, but what stayed with me most was working as a team against a deadline: splitting the work so everyone could move fast, and accepting that some things, like our front-end, just won’t make it in time.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>life</category></item></channel></rss>