[{"content":" A junior developer writes code that assumes everything will go right (no checks).\nA mid-level developer writes code that assumes everything will go wrong (defensive checks and assertions everywhere).\nA senior developer uses the type system to make going wrong impossible, deleting 80% of those checks entirely.\n— Software Engineering Proverb\n📌 TL;DR Validate at the edges (Defensive): Expect bad data from the outside world. Handle it gracefully using the val / ok pattern (the Result pattern) instead of throwing unpredictable try/catch errors. Assert in the core (Offensive): Expect perfect data internally. If your internal state is wrong, it’s a system bug. Use TypeScript\u0026rsquo;s asserts keyword to crash immediately (fail-fast). Parse, Don\u0026rsquo;t Validate: Don\u0026rsquo;t just check data; transform it at the boundary into Branded Types (e.g., EmailAddress, ValidatedUserId). The Result: Your core business logic functions only accept Branded Types. This makes invalid states mathematically impossible to represent, allowing you to delete hundreds of lines of defensive if (!data) checks. If you’ve spent enough time writing Node.js or TypeScript, you’ve likely written a function that looks exactly like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // ❌ The Mid-Level \u0026#34;Defensive\u0026#34; Controller async function processRefund(req: Request, res: Response) { try { const body = await req.json(); // ⚠️ Throws if malformed JSON! // Defensive checks everywhere... if (!body || typeof body !== \u0026#39;object\u0026#39;) { throw new Error(\u0026#34;Invalid payload\u0026#34;); } if (!body.userId || typeof body.userId !== \u0026#39;string\u0026#39;) { throw new Error(\u0026#34;Missing UserId\u0026#34;); } if (typeof body.amount !== \u0026#39;number\u0026#39; || body.amount \u0026lt;= 0) { throw new Error(\u0026#34;Invalid amount\u0026#34;); } const user = await db.getUser(body.userId); if (!user) { throw new Error(\u0026#34;User not found\u0026#34;); // Expected error? Or DB bug? } // Finally... the actual business logic await gateway.refund(user.stripeId, body.amount); return res.status(200).send(\u0026#34;Success\u0026#34;); } catch (error) { // ⚠️ Is this a 400 bad request? Or a 500 DB failure? We don\u0026#39;t know anymore. return res.status(400).send({ error: error.message }); } } This code is exhausting to read. The core business logic is buried under a mountain of defensive if/throw statements. Furthermore, the catch block has no idea if the error was a user making a typo (400 Bad Request) or the database catching fire (500 Internal Error).\nError handling isn’t just about catching mistakes; it’s about system architecture.\nTo fix this, we need to understand the fundamental difference between Validation and Assertion, adopt the Result pattern, and use Branded Types to push errors to the absolute edges of our system.\nRule 1: Learn the Difference Between Validation and Assertion The most important architectural distinction you can make is understanding the difference between validating data and asserting state.\nThink of your application like an exclusive Nightclub.\nFeature Validation Assertion Location The Front Door (API/IO Boundary) The VIP Room (Core Logic) Expectation Bad data is expected Data is trusted Philosophy Defensive (Bouncer) Offensive (Security Guard) Outcome Graceful Recovery (400 Bad Request) Immediate Crash (500 System Error) Validation is the bouncer at the front door. The bouncer expects people to hand him fake IDs or be underage. He calmly turns them away. Validation inspects untrusted external input and recovers gracefully.\nAssertion is the security guard deep inside the VIP room. The guard expects everyone in the room to have a VIP wristband. If someone is in the room without one, the bouncer system has fundamentally failed. The guard stops the music and shuts down the party. Assertions inspect internal logic and fail fast.\nWhen you mix these up, systems become fragile. If you crash the app when a user types a bad email, you have a terrible UX. If you try to \u0026ldquo;gracefully recover\u0026rdquo; when a database query returns an impossible state—like a negative account balance—you silently corrupt your data.\nRule 2: At the Edge, Treat Errors as Values When data arrives from the outside world (API input, DB reads), it is untrusted. Because we expect bad data, throwing exceptions is an anti-pattern. throw is essentially a hidden GOTO statement that destroys TypeScript\u0026rsquo;s type safety (the error in a catch block is always typed as unknown).\nInstead, we use the val / ok pattern (the Result pattern), heavily inspired by Go and Rust. We treat errors as standard return values.\n1 2 3 4 5 6 7 8 9 10 11 12 13 // The Result Type Blueprint type Result\u0026lt;T, E = Error\u0026gt; = | { ok: true; value: T } | { ok: false; error: E }; // ✅ Wrap the chaotic edge in a predictable Result async function parseJson(req: Request): Promise\u0026lt;Result\u0026lt;unknown, string\u0026gt;\u0026gt; { try { return { ok: true, value: await req.json() }; } catch (err) { return { ok: false, error: \u0026#34;Malformed JSON\u0026#34; }; } } By returning a Result, the TypeScript compiler forces the caller to handle the failure before they are allowed to access value. We\u0026rsquo;ve eliminated the silent boundary leak.\nRule 3: Parse, Don\u0026rsquo;t Validate Now that we have safely parsed the JSON, we need to ensure it has the correct shape. But traditional validation has a massive flaw: it doesn\u0026rsquo;t leave a receipt.\nIf you write a function isValidEmail(input): boolean, and it returns true, TypeScript still just sees a string. If you pass that string down through five other files, none of those files know it was validated. So, mid-level developers defensively re-validate the string everywhere.\nTo fix this, we use the \u0026ldquo;Parse, Don\u0026rsquo;t Validate\u0026rdquo; paradigm 1. We use schema parsers (like Zod 2) combined with Branded Types to make invalid states impossible to represent.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { z } from \u0026#34;zod\u0026#34;; // 1. The Branded Types (The VIP Wristbands) type ValidatedUserId = string \u0026amp; { readonly __brand: unique symbol }; type PositiveAmount = number \u0026amp; { readonly __brand: unique symbol }; // 2. The Smart Constructor (The Bouncer) // This schema does not just check data; it transforms and Brands it. const RefundSchema = z.object({ userId: z.string().uuid().transform(val =\u0026gt; val as ValidatedUserId), amount: z.number().positive().transform(val =\u0026gt; val as PositiveAmount) }); // A helper to turn Zod into our Result pattern function parseRefundRequest(data: unknown): Result\u0026lt;z.infer\u0026lt;typeof RefundSchema\u0026gt;, string\u0026gt; { const parsed = RefundSchema.safeParse(data); if (!parsed.success) return { ok: false, error: parsed.error.message }; return { ok: true, value: parsed.data }; } Rule 4: Inside the Core, Assert and Crash Once data has passed the Smart Constructor, it is inside our trusted domain. We shouldn\u0026rsquo;t be handling expected validation errors anymore. We are dealing with internal system invariants.\nIf an assumption is wrong here, we want to crash. TypeScript makes this powerful with the asserts keyword, which permanently narrows types for the remainder of a scope.\n1 2 3 4 5 6 7 // A utility to bridge Result types back into assertions for trusted zones function assertOk\u0026lt;T\u0026gt;(result: Result\u0026lt;T, Error\u0026gt;): asserts result is { ok: true; value: T } { if (!result.ok) { // CRASH! The VIP guard shuts down the party. throw result.error; } } The Grand Architecture: Layers of Trust Let\u0026rsquo;s look at the \u0026ldquo;Before\u0026rdquo; code from the beginning of this article, refactored into the Layers of Trust architecture.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 // --- 1. THE CORE DOMAIN (Zero validation checks!) --- // Because we demand Branded Types, it is mathematically impossible // to call this function with dirty data. async function executeRefund(userId: ValidatedUserId, amount: PositiveAmount) { const dbResult = await db.getUser(userId); // ASSERTION: We assume the DB works and the user exists. // If not, our system is broken. Fail-fast! assertOk(dbResult); // TypeScript guarantees dbResult.value is our User object. await gateway.refund(dbResult.value.stripeId, amount); } // --- 2. THE EDGE CONTROLLER --- async function processRefundRoute(req: Request, res: Response) { // Layer 1: Parse the chaotic outside world safely const jsonResult = await parseJson(req); if (!jsonResult.ok) { return res.status(400).send({ error: jsonResult.error }); } // Layer 2: Smart Constructor (Validate and Brand) const payloadResult = parseRefundRequest(jsonResult.value); if (!payloadResult.ok) { return res.status(400).send({ error: payloadResult.error }); } // Layer 3: Execute Core Logic try { // TS knows payloadResult.value contains our Branded Types! await executeRefund(payloadResult.value.userId, payloadResult.value.amount); return res.status(200).send(\u0026#34;Success\u0026#34;); } catch (error) { // If we end up here, an internal ASSERTION tripped. // This is a real bug. Page the developer! logCriticalBug(error); return res.status(500).send(\u0026#34;Internal Server Error\u0026#34;); } } The Takeaway Look at the executeRefund function above. It is completely pure. There are no if statements checking for empty strings. There are no typeof checks. Your cognitive load drops to zero.\nTo stop fighting TypeScript and start leveraging it as an architectural tool, memorize this paradigm:\nValidation is Defensive. You expect the outside world to be messy. Use the val / ok pattern to parse data, gracefully recover, and return user-friendly 400-level errors. Assertion is Offensive. You expect your internal logic to be flawless. Use asserts to fail-fast, crash, and return 500-level errors when system invariants are broken. Encode Trust in Types. Push validation to the edges, use Smart Constructors to create Branded Types, make invalid states unrepresentable, and delete the rest of your runtime checks. Correctness is so important that any violation of internal logic is a bug. Build strict boundaries, trust your types, and watch your codebase become infinitely more resilient.\nThis concept was popularized by Alexis King in her seminal essay Parse, Don\u0026rsquo;t Validate.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nZod is a TypeScript-first schema declaration and validation library.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026-04-13T00:00:00Z","permalink":"/p/stop-validating-everywhere-an-architectural-guide-to-error-handling-in-typescript/","title":"Stop Validating Everywhere: An Architectural Guide to Error Handling in TypeScript"},{"content":"This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.\nHeadings The following HTML \u0026lt;h1\u0026gt;—\u0026lt;h6\u0026gt; elements represent six levels of section headings. \u0026lt;h1\u0026gt; is the highest section level while \u0026lt;h6\u0026gt; is the lowest.\nH1 H2 H3 H4 H5 H6 Paragraph Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.\nItatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.\nBlockquotes The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.\nBlockquote without attribution Tiam, ad mint andaepu dandae nostion secatur sequo quae. Note that you can use Markdown syntax within a blockquote.\nBlockquote with attribution Don\u0026rsquo;t communicate by sharing memory, share memory by communicating.\n— Rob Pike1\nTables Tables aren\u0026rsquo;t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.\nName Age Bob 27 Alice 23 Inline Markdown within tables Italics Bold Code italics bold code A B C D E F Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus Proin sit amet velit nec enim imperdiet vehicula. Ut bibendum vestibulum quam, eu egestas turpis gravida nec Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien Code Blocks Code block with backticks 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block indented with four spaces \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026quot;en\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Diff code block 1 2 3 4 5 [dependencies.bevy] git = \u0026#34;https://github.com/bevyengine/bevy\u0026#34; rev = \u0026#34;11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13\u0026#34; - features = [\u0026#34;dynamic\u0026#34;] + features = [\u0026#34;jpeg\u0026#34;, \u0026#34;dynamic\u0026#34;] One line code block 1 \u0026lt;p\u0026gt;A paragraph\u0026lt;/p\u0026gt; List Types Ordered List First item Second item Third item Unordered List List item Another item And another item Nested list Fruit Apple Orange Banana Dairy Milk Cheese Other Elements — abbr, sub, sup, kbd, mark GIF is a bitmap image format.\nH2O\nXn + Yn = Zn\nPress CTRL + ALT + Delete to end the session.\nMost salamanders are nocturnal, and hunt for insects, worms, and other small creatures.\nThe above quote is excerpted from Rob Pike\u0026rsquo;s talk during Gopherfest, November 18, 2015.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-09-07T00:00:00Z","permalink":"/p/markdown-syntax-guide/","title":"Markdown Syntax Guide"},{"content":"Hugo theme Stack supports the creation of interactive image galleries using Markdown. It\u0026rsquo;s powered by PhotoSwipe and its syntax was inspired by Typlog.\nTo use this feature, the image must be in the same directory as the Markdown file, as it uses Hugo\u0026rsquo;s page bundle feature to read the dimensions of the image. External images are not supported.\nSyntax 1 ![Image 1](1.jpg) ![Image 2](2.jpg) Result Photo by mymind and Luke Chesser on Unsplash\n","date":"2023-08-26T00:00:00Z","image":"/p/image-gallery/2.jpg","permalink":"/p/image-gallery/","title":"Image gallery"},{"content":"For more details, check out the documentation.\nBilibili video Tencent video YouTube video Generic video file Your browser doesn't support HTML5 video. Here is a link to the video instead. GitLab Quote Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n― A famous person, The book they wrote Photo by Codioful on Unsplash\n","date":"2023-08-25T00:00:00Z","image":"/p/shortcodes/cover.jpg","permalink":"/p/shortcodes/","title":"Shortcodes"},{"content":"Stack has built-in support for math typesetting using KaTeX.\nIt\u0026rsquo;s not enabled by default side-wide, but you can enable it for individual posts by adding math: true to the front matter. Or you can enable it side-wide by adding math = true to the params.article section in config.toml.\nInline math This is an inline mathematical expression: $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$\n1 $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$ Block math $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$ 1 2 3 $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$ $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi $$ 1 2 3 $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi $$ ","date":"2023-08-24T00:00:00Z","permalink":"/p/math-typesetting/","title":"Math Typesetting"}]