<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mihai Serban | Software Engineer]]></title><description><![CDATA[Software engineer sharing insights on JavaScript, React, AWS, mobile development, and building products.]]></description><link>https://mihaiserban.dev</link><generator>GatsbyJS</generator><lastBuildDate>Thu, 09 Jul 2026 00:20:50 GMT</lastBuildDate><atom:link href="https://mihaiserban.dev/atom.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[A 7K-Parameter Neural Router That Saved Me Money on AI Coding]]></title><description><![CDATA[How I trained a 5,000-parameter linear head on top of Qwen-0.6B to classify coding requests by task type, routing cheap models to exploration and reserving expensive ones for complex builds.]]></description><link>https://mihaiserban.dev/blog/neural-llm-router-coding-assistant/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/neural-llm-router-coding-assistant/</guid><category><![CDATA[llm]]></category><category><![CDATA[router]]></category><category><![CDATA[coding]]></category><category><![CDATA[ai-agents]]></category><pubDate>Tue, 07 Jul 2026 05:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I run an AI gateway that sits between my coding agents and a pool of LLM providers. The gateway handles health checks, failover, and model resolution. But it had one blind spot: it never looked at the &lt;em&gt;content&lt;/em&gt; of what I was asking.&lt;/p&gt;
&lt;p&gt;Every request was routed based on whatever model the agent picked — opencode would say &quot;I think this is a coding task, use &lt;code class=&quot;language-text&quot;&gt;coder&lt;/code&gt;&quot; and the gateway would oblige. That works, but it wastes money. An exploration task (&quot;search the codebase for User model references&quot;) doesn&apos;t need a reasoning-heavy $0.56/M output model. A &lt;code class=&quot;language-text&quot;&gt;deepseek-v4-flash&lt;/code&gt; at 20x cheaper would do fine.&lt;/p&gt;
&lt;p&gt;So I built a neural router that reads the &lt;em&gt;first user message&lt;/em&gt; and classifies it into one of four task types: &lt;strong&gt;explore&lt;/strong&gt;, &lt;strong&gt;plan&lt;/strong&gt;, &lt;strong&gt;build&lt;/strong&gt;, or &lt;strong&gt;quick&lt;/strong&gt;. Based on the task type, the gateway picks the right model pool and reasoning effort. The classifier: a 5,120-parameter linear layer on top of Qwen-0.6B.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The architecture&lt;/h2&gt;
&lt;p&gt;The router is a sidecar service. The gateway calls it over HTTP, falls back to the default model if it&apos;s unreachable, and opens a circuit breaker after the first failure.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;openCode/Codex → gateway :4100 → router sidecar :5560 → Qwen3-0.6B → head → {task,reasoning}
                                  │
                                  └─ (fallback) config.default_model&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The head is borrowed from &lt;a href=&quot;https://arxiv.org/abs/2512.04695&quot;&gt;TRINITY&lt;/a&gt; (Xu et al., ICLR 2026): a single weight matrix &lt;code class=&quot;language-text&quot;&gt;W ∈ R^{7×1024}&lt;/code&gt; — no bias, no activation. Seven logits split into two softmax groups:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Dimensions&lt;/th&gt;
&lt;th&gt;Outputs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Task type&lt;/td&gt;
&lt;td&gt;4 logits&lt;/td&gt;
&lt;td&gt;explore, plan, build, quick&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning effort&lt;/td&gt;
&lt;td&gt;3 logits&lt;/td&gt;
&lt;td&gt;low, medium, high&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;At inference, the encoder runs the user message through Qwen3-0.6B, extracts a 1024-dimensional hidden state from the final layer (mean-pooled across all tokens, L2-normalized), and multiplies by the head. That&apos;s it. The output maps 1-to-1 into the gateway&apos;s routing config:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;task_to_combo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;explore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; explorer     &lt;span class=&quot;token comment&quot;&gt;# fast/cheap models (deepseek-v4-flash)&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; planner         &lt;span class=&quot;token comment&quot;&gt;# reasoning models (glm-5.2, deepseek-v4-pro)&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; coder          &lt;span class=&quot;token comment&quot;&gt;# powerful coding models (kimi-k2.7-code)&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;quick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; coder&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;fast     &lt;span class=&quot;token comment&quot;&gt;# cheap models for commits, bash commands&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;task_to_reasoning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;explore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; low
  &lt;span class=&quot;token key atrule&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; high
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; high
  &lt;span class=&quot;token key atrule&quot;&gt;quick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; low&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2&gt;Training data came from my own sessions&lt;/h2&gt;
&lt;p&gt;I didn&apos;t have to collect new data. opencode stores every session in a SQLite database at &lt;code class=&quot;language-text&quot;&gt;~/.local/share/opencode/opencode.db&lt;/code&gt; with 18,000+ messages across 500 sessions. Each session has an &lt;code class=&quot;language-text&quot;&gt;agent&lt;/code&gt; field — the task type opencode already inferred:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;session&lt;/span&gt; s
&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; message m &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;session_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; part p &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agent &lt;span class=&quot;token operator&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;plan&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;explore&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;librarian&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;general&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; json_extract&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;$.role&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;user&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; json_extract&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;$.type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;text&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;agent&lt;/code&gt; field was my label. I mapped it:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;opencode agent&lt;/th&gt;
&lt;th&gt;Task type&lt;/th&gt;
&lt;th&gt;Sessions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;explore, librarian&lt;/td&gt;
&lt;td&gt;explore&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plan&lt;/td&gt;
&lt;td&gt;plan&lt;/td&gt;
&lt;td&gt;192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;383&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build (commit, bash, git, etc.)&lt;/td&gt;
&lt;td&gt;quick&lt;/td&gt;
&lt;td&gt;181&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;quick&lt;/code&gt; split is heuristic — any build message containing &quot;commit&quot;, &quot;git push&quot;, &quot;bash&quot;, &quot;run&quot;, etc. got labeled quick instead. 809 labeled samples total.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Training: from 47% to 79%&lt;/h2&gt;
&lt;p&gt;First pass was disappointing. Penultimate-token encoding + SGD got stuck at 47.5% task accuracy. The hidden states for different task types were almost indistinguishable — all coding prompts look similar under cosine similarity (0.3–0.5 within-class and between-class alike).&lt;/p&gt;
&lt;p&gt;Two changes fixed it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mean pooling instead of penultimate token.&lt;/strong&gt; The penultimate token only captures the last word&apos;s context. Mean-pooling across all tokens carries the full query semantics. The paper uses penultimate for multi-turn transcripts where the last word is the role signal — but for single-message classification, mean pooling works better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adam with class-weighted loss.&lt;/strong&gt; The build class dominated (383 samples vs. 53 explore). Without weights, the head leaned toward build. With inverse-frequency class weights and Adam at lr=0.001, convergence was smooth:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Epoch&lt;/th&gt;
&lt;th&gt;Task accuracy&lt;/th&gt;
&lt;th&gt;Reasoning accuracy&lt;/th&gt;
&lt;th&gt;Loss&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;67.3%&lt;/td&gt;
&lt;td&gt;86.4%&lt;/td&gt;
&lt;td&gt;1.47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;73.3%&lt;/td&gt;
&lt;td&gt;88.9%&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;78.6%&lt;/td&gt;
&lt;td&gt;91.2%&lt;/td&gt;
&lt;td&gt;0.73&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The reasoning accuracy is high because the labels are deterministic (task→reasoning mapping), but the head still learns meaningful signal from the message content.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Inference on Apple Silicon&lt;/h2&gt;
&lt;p&gt;Qwen3-0.6B runs on MPS. The entire stack — encoder + head — is a single Python process with FastAPI:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/route&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RouteRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; RouteResponse&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    h &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; encoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;encode_mean_pool&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transcript&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    task_type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reasoning&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; debug &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;select&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;torch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;as_tensor&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;h&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; RouteResponse&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;task_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;task_type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reasoning_effort&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;reasoning&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Latency on an M3 Max: &lt;strong&gt;96ms warm, 624ms cold start&lt;/strong&gt;. The head weights file is 30KB.&lt;/p&gt;
&lt;p&gt;Sample predictions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;Search the codebase for User model&quot; → explore (70.4%) low (51.0%)
&quot;Write a function to parse markdown&quot;  → build   (68.5%) high (75.8%)
&quot;Commit changes with fix message&quot;     → quick   (66.2%) high (51.0%)
&quot;Design a real-time chat architecture&quot;→ build   (40.3%) high (88.2%)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The plan/build boundary is the hardest — &quot;design system architecture&quot; and &quot;write a function&quot; both look like structured tasks with similar vocabulary. The head leans toward build for anything code-related, which is the safe default since a coding task can&apos;t be ruined by too much capability.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What&apos;s next&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment-level optimization.&lt;/strong&gt; Right now the router picks a combo bucket. The next step is per-deployment scoring within a combo — the cheapest available provider that&apos;s healthy and close to the latency target gets the request. This is where CMA-ES (the TRINITY paper&apos;s training algorithm) comes in, optimizing for &lt;code class=&quot;language-text&quot;&gt;quality - λ × cost&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-improving C-A-F loop.&lt;/strong&gt; The &lt;a href=&quot;https://arxiv.org/abs/2606.22902&quot;&gt;Agent-as-a-Router&lt;/a&gt; paper (Zhou et al., 2026) describes a Context-Action-Feedback loop where the router learns from every outcome. My gateway already logs usage events to Postgres — token counts, latency, served deployment, cache hits. Adding a Verifier (execution success) and a Memory module (vector store of past routing decisions) turns the router from a static classifier into an online learner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoiding routing collapse.&lt;/strong&gt; The &lt;a href=&quot;https://arxiv.org/abs/2602.03478&quot;&gt;When Routing Collapses&lt;/a&gt; paper (Lai &amp;#x26; Ye, 2026) shows a pervasive failure mode: routers default to the most expensive model as the cost budget increases, even when cheaper models suffice. Their fix — learning rankings instead of scalar scores — is something I&apos;ll adopt when moving to deployment-level routing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Bottom line&lt;/h2&gt;
&lt;p&gt;A 5,120-parameter linear layer trained on my own 809 coding sessions classifies requests with enough accuracy to route cheap models to exploration tasks. The entire router runs locally on Apple Silicon in under 100ms. If the router dies, the gateway doesn&apos;t care — it just falls back to the safe default.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Replacing Orchestrator Agents: What arXiv Research Says About Context Bottlenecks and Better Alternatives]]></title><description><![CDATA[Orchestrator agent workflows break down on long-horizon tasks because a single context window becomes the hard ceiling. I researched 11 arXiv papers and replaced my orchestrator-minion architecture with a file-based governance-worker-synthesis system.]]></description><link>https://mihaiserban.dev/blog/replacing-orchestrator-agents-with-governance-worker-synthesis/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/replacing-orchestrator-agents-with-governance-worker-synthesis/</guid><category><![CDATA[ai-agents]]></category><category><![CDATA[llm]]></category><category><![CDATA[multi-agent]]></category><category><![CDATA[context-window]]></category><category><![CDATA[research]]></category><pubDate>Sun, 05 Jul 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My coding agents use a pattern I suspect many of you recognize: an orchestrator agent plans the work, spawns minion sub-agents, collects their results, and synthesizes the final answer. It works fine for short tasks. But on anything long-horizon — a multi-file refactor, a cross-repo analysis, or a research sprint — it falls apart.&lt;/p&gt;
&lt;p&gt;The orchestrator&apos;s context window becomes the hard ceiling. Every minion result gets dumped back into the same window. By the third or fourth sub-task, the orchestrator is drowning in accumulated context and accuracy collapses. The pattern is fundamentally flawed.&lt;/p&gt;
&lt;p&gt;I decided to tear it out and replace it. But first, I wanted evidence — not vibes. Here&apos;s what 11 arXiv papers told me.&lt;/p&gt;
&lt;h2&gt;The evidence against orchestrators&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2606.31174&quot;&gt;ClawArena-Team&lt;/a&gt;&lt;/strong&gt; (Jun 2026) built a benchmark specifically to measure a single LLM&apos;s ability to manage sub-agents. Their finding: &lt;strong&gt;no model exceeds 50% workspace-permission precision.&lt;/strong&gt; The bottleneck isn&apos;t model capability — it&apos;s the architecture of delegating through a single constrained context window. Cost and management quality were essentially decoupled: API spend varied over 100x while management scores clustered in a tight 9.9-point band.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2505.21471&quot;&gt;ExtAgents&lt;/a&gt;&lt;/strong&gt; (ACL 2026) identified two core bottlenecks in existing agent orchestration designs and showed that distributing knowledge input across multiple agents in parallel — rather than funneling everything through one orchestrator — significantly outperforms single-context approaches, regardless of whether the input fits in the context window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2604.01020&quot;&gt;OrgAgent&lt;/a&gt;&lt;/strong&gt; (Apr 2026) compared flat multi-agent systems against hierarchical ones organized like a company — governance, execution, and compliance layers. The hierarchical structure improved performance by &lt;strong&gt;102.73%&lt;/strong&gt; while reducing token usage by &lt;strong&gt;74.52%&lt;/strong&gt; on SQuAD 2.0. The key insight: governance should plan and allocate, not micromanage execution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2511.02424&quot;&gt;ReAcTree&lt;/a&gt;&lt;/strong&gt; (AAMAS 2026) attacked the long-horizon problem directly. Monolithic ReAct-style trajectories entangle all past decisions and observations; ReAcTree decomposes complex goals into a dynamically constructed agent tree where each node handles a subgoal independently. On WAH-NL, it achieved &lt;strong&gt;61% goal success rate vs 31% for ReAct&lt;/strong&gt; with Qwen 2.5 72B — nearly double.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2606.30524&quot;&gt;The Illusion of Agentic Complexity&lt;/a&gt;&lt;/strong&gt; (ICSME 2026) delivered a sobering finding: for README generation, a single-agent pipeline matched multi-agent quality while &lt;strong&gt;reducing token consumption by 86% and running at 2x speed.&lt;/strong&gt; Multi-agent only won on structural consistency. The sobering conclusion: more agents is not uniformly better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2606.30986&quot;&gt;The Organizational Behavior of Agentic AI&lt;/a&gt;&lt;/strong&gt; (Jun 2026) was perhaps the most actionable paper. It found that human-imitation coordination forms — managers, committees, supervisor-worker — consistently underperform because they add lossy handoffs, correlated deliberation, and verification burdens. &lt;strong&gt;Shared-state and adaptive forms perform better&lt;/strong&gt; when they make context durable, inspectable, and task-contingent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2607.01523&quot;&gt;Multi-Head Recurrent Memory&lt;/a&gt;&lt;/strong&gt; (Jul 2026) diagnosed why recurrent memory agents degrade: monolithic text-block memory causes overwrite collisions as context grows. By partitioning memory into independent heads with a select-then-update strategy — only one head updated per step, others shielded from overwriting — they improved retention from &amp;#x3C;30% to 73.96% at 896K tokens, training-free.&lt;/p&gt;
&lt;h2&gt;Three proven alternative patterns&lt;/h2&gt;
&lt;p&gt;Across the 11 papers, three patterns keep emerging:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Key insight&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hierarchical decomposition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Split planning from execution; each sub-agent gets a fresh context window&lt;/td&gt;
&lt;td&gt;Long-horizon tasks with natural subtask boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distributional/parallel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distribute input across agents, don&apos;t funnel through one&lt;/td&gt;
&lt;td&gt;Tasks where knowledge exceeds a single context window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared-state + adaptive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agents share durable filesystem state, not chat messages&lt;/td&gt;
&lt;td&gt;Tasks requiring coordination without central control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The common thread: &lt;strong&gt;get data out of the context window and onto durable, inspectable storage.&lt;/strong&gt; Chat-based handoffs between agents are the hidden cost. A YAML file on disk that a fresh agent reads is cheaper — in both tokens and accuracy — than a 50-line natural-language summary passed through a bloated context.&lt;/p&gt;
&lt;h2&gt;What I built: governance-worker-synthesis&lt;/h2&gt;
&lt;p&gt;I replaced my orchestrator-minion system with a three-agent architecture backed by filesystem state:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;User → Governance (plans, writes task specs to .agent-state/tasks/*.yaml)
         │
         │  spawns workers with: &quot;Read task spec. Write result to file. Confirm.&quot;
         ▼
    ┌──────────┐  ┌──────────┐  ┌──────────┐
    │ Worker 1 │  │ Worker 2 │  │ Worker 3 │   ← fresh contexts
    └────┬─────┘  └────┬─────┘  └────┬─────┘
         │             │             │
         ▼             ▼             ▼   (writes to disk, returns short confirmation)
    .agent-state/results/*.yaml          ← shared filesystem state
         │
         ▼
    Synthesis agent (fresh context, reads result files, produces final answer)
         │
         ▼
    User ← clean, focused output&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Governance&lt;/strong&gt; never does per-unit work and never synthesizes results. It only plans and delegates. Each worker brief is minimal: a task spec path and a result path. Workers write structured results to YAML files and return only a short confirmation — not the full result. When all workers complete, a fresh synthesis agent reads every result file and produces the final answer.&lt;/p&gt;
&lt;p&gt;The task spec schema:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .agent-state/tasks/analyze-auth.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; audit the authentication module for security issues
&lt;span class=&quot;token key atrule&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; do not modify the database schema
&lt;span class=&quot;token key atrule&quot;&gt;expected_output_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .agent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;state/results/analyze&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auth.yaml
&lt;span class=&quot;token key atrule&quot;&gt;context_files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;src/auth/middleware.ts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; src/auth/session.ts&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;blocked_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result schema:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .agent-state/results/analyze-auth.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ok
&lt;span class=&quot;token key atrule&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; found two session fixation vulnerabilities and one missing CSRF check
&lt;span class=&quot;token key atrule&quot;&gt;findings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; session tokens not rotated on login (critical)
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; cookie flagged httpOnly but not secure (medium)
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; CSRF middleware bypassed on API routes (high)
&lt;span class=&quot;token key atrule&quot;&gt;files_changed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;blockers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;verification_gaps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; could not verify third&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;party OAuth callback flow end&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The plugin I wrote (replacing the old orchestrator-minion watchdog) adds two capabilities beyond inactivity detection:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fanout completion tracking&lt;/strong&gt; — when all workers for a given fanout are done, the plugin nudges governance to spawn synthesis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path-aware recovery&lt;/strong&gt; — when a worker stalls, the recovery prompt includes the task spec path and expected result path, so governance can inspect the state on disk before deciding what to do.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Recovery prompt now includes file paths&lt;/span&gt;
&quot;&lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt; watched worker may be inactive&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
 Task spec&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agent&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;state&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;tasks&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;analyze&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yaml
 Expected result&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agent&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;state&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;results&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;analyze&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yaml
 Inspect the child session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; then decide&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; wait&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; re&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;brief&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; or report a blocker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;What changed&lt;/h2&gt;
&lt;p&gt;The before-and-after on a typical multi-file refactor (analyze auth, fix session handling, audit deps):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before (orchestrator-minion)&lt;/th&gt;
&lt;th&gt;After (governance-worker-synthesis)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context at peak&lt;/td&gt;
&lt;td&gt;~85K tokens (3 minion results accumulated)&lt;/td&gt;
&lt;td&gt;~12K tokens (3 short confirmations)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handoff format&lt;/td&gt;
&lt;td&gt;Natural language summaries&lt;/td&gt;
&lt;td&gt;Structured YAML on disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Synthesis context&lt;/td&gt;
&lt;td&gt;Same bloated orchestrator window&lt;/td&gt;
&lt;td&gt;Fresh, clean window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale worker recovery&lt;/td&gt;
&lt;td&gt;Blind &quot;inspect the session&quot;&lt;/td&gt;
&lt;td&gt;Path-aware with task spec + result file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orchestrator drift&lt;/td&gt;
&lt;td&gt;Frequent — &quot;just this one fix&quot;&lt;/td&gt;
&lt;td&gt;Impossible — governance can&apos;t execute&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The synthesis agent is the sleeper hit. Giving it a fresh context window dedicated solely to reading result files and producing output means it never sees the governance planning phase, the worker failures, or the recovery nudges. It sees only the finished work, and its output quality reflects that.&lt;/p&gt;
&lt;h2&gt;What still needs work&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Elastic context (ACE).&lt;/strong&gt; The &lt;a href=&quot;https://arxiv.org/abs/2606.31564&quot;&gt;ACE paper&lt;/a&gt; describes a pluggable module that maintains lossless message history with adaptive compression — raw for recent steps, compressed abstracts for older ones, dropped for irrelevant ones. I haven&apos;t implemented this yet. For long sessions where governance itself accumulates context (e.g., a 20-worker fanout), this would be the next layer of defense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dependency-aware fanout.&lt;/strong&gt; Right now, fanout assumes all workers are fully independent. If task B needs task A&apos;s output, governance must serialize them manually. ReAcTree&apos;s dynamic tree construction could automate this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Structured handoff standardization.&lt;/strong&gt; The YAML schemas I use are project-specific. There&apos;s room for a small, shared vocabulary — &lt;code class=&quot;language-text&quot;&gt;status&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;findings&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;files_changed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;blockers&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;verification_gaps&lt;/code&gt; — that any agent in any project could use.&lt;/p&gt;
&lt;h2&gt;Bottom line&lt;/h2&gt;
&lt;p&gt;Orchestrator agents are a local maximum. They feel right because they mirror how humans manage teams. But the arXiv research is clear: shared-state coordination beats chat-based delegation, hierarchical decomposition beats monolithic context, and structured handoffs beat natural-language summaries.&lt;/p&gt;
&lt;p&gt;The governance-worker-synthesis pattern isn&apos;t the final answer, but it&apos;s a concrete step away from the dead end of orchestrator context bloat. If your agent system has an orchestrator that touches per-unit work or accumulates sub-agent results in its own window, the papers are clear: that&apos;s your bottleneck, not your model.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Papers referenced: &lt;a href=&quot;https://arxiv.org/abs/2606.31174&quot;&gt;ClawArena-Team&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2505.21471&quot;&gt;ExtAgents&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2604.01020&quot;&gt;OrgAgent&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2511.02424&quot;&gt;ReAcTree&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2606.30524&quot;&gt;Illusion of Agentic Complexity&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2606.30986&quot;&gt;Org Behavior of Agentic AI&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2607.01523&quot;&gt;Multi-Head Recurrent Memory&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2606.31564&quot;&gt;ACE&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2510.07423&quot;&gt;ProSEA&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2604.27358&quot;&gt;Safe Bilevel Delegation&lt;/a&gt;, &lt;a href=&quot;https://arxiv.org/abs/2512.08545&quot;&gt;Curriculum Guided Massive MAS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building a Design Pattern Generator for CNC & Laser Cutting]]></title><description><![CDATA[I built a web-based pattern generator to design perforated sheet metal patterns for my front gate. It exports SVG, PDF, and DXF — and runs entirely in the browser.]]></description><link>https://mihaiserban.dev/blog/building-a-design-pattern-generator-for-cnc-laser-cutting/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/building-a-design-pattern-generator-for-cnc-laser-cutting/</guid><category><![CDATA[react]]></category><category><![CDATA[gatsby]]></category><category><![CDATA[cnc]]></category><category><![CDATA[laser-cutting]]></category><category><![CDATA[svg]]></category><category><![CDATA[dxf]]></category><pubDate>Tue, 09 Jun 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I needed a front gate. Not just any gate — a metal gate with a perforated pattern that lets light through while keeping privacy. The kind of thing you see on modern architectural builds, where the sheet metal is laser-cut with a gradient of holes or lines that fade from dense to sparse.&lt;/p&gt;
&lt;p&gt;I called a few laser-cutting shops. Every single one asked the same thing: &lt;em&gt;&quot;Can you send us the CAD file?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I didn&apos;t have one. I had a mental image — something that gets denser at the bottom and fades toward the top, with rounded corners on the cutouts. Drawing that manually in CAD software, iterating on spacing and density, exporting to DXF, then realizing the proportions were off... that loop sounded like hours of pain. And I wanted to &lt;em&gt;see&lt;/em&gt; the pattern before committing to a full sheet of metal.&lt;/p&gt;
&lt;p&gt;So I did what any developer with a deadline and a stubborn streak would do: I built a tool.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Problem: From Idea to Cut File&lt;/h2&gt;
&lt;p&gt;Laser and CNC machines are incredibly precise, but they are dumb. They follow paths. The creative part — deciding where those paths go — is entirely on you. Most workflows look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sketch an idea on paper.&lt;/li&gt;
&lt;li&gt;Open Illustrator / Inkscape / AutoCAD.&lt;/li&gt;
&lt;li&gt;Draw shapes manually.&lt;/li&gt;
&lt;li&gt;Copy, paste, nudge, guess at spacing.&lt;/li&gt;
&lt;li&gt;Export to DXF or PDF.&lt;/li&gt;
&lt;li&gt;Send to the shop.&lt;/li&gt;
&lt;li&gt;Realize the scale is wrong. Go back to step 3.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I wanted to collapse that loop into a single web page where I could tweak parameters and immediately see the result, measured in real millimeters, ready to export.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What I Built&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;/design-pattern-generator&quot;&gt;Design Pattern Generator&lt;/a&gt; is a standalone React app embedded in my Gatsby site. It runs entirely in the browser — no backend, no uploads, no accounts. Everything is local.&lt;/p&gt;
&lt;h3&gt;Core Features&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Live SVG preview&lt;/strong&gt; — Every change updates a vector preview in real time. Dimensions are exact: if you set the canvas to 1000 mm × 2000 mm, the preview and the exported file match that precisely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Four shape types&lt;/strong&gt; — Circles, squares, horizontal lines, and vertical lines. Lines are particularly useful for slatted designs where you want airflow or visibility control. Squares support rounded corners. Lines support variable length and corner radius.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gradient density control&lt;/strong&gt; — This is where the tool gets interesting. You can set a global density (0–100%), but you can also apply a directional gradient:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uniform&lt;/li&gt;
&lt;li&gt;Left-to-right, right-to-left&lt;/li&gt;
&lt;li&gt;Top-to-bottom, bottom-to-top&lt;/li&gt;
&lt;li&gt;Radial (dense in the center, fading outward)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The gradient uses a curved falloff so the fade feels natural, not mechanically linear. The sparse end of the gradient never drops to zero — it stays around 10% of the max density so the pattern doesn&apos;t completely vanish.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Floyd-Steinberg dithering&lt;/strong&gt; — To avoid the &quot;grid of dots&quot; look, the engine uses Floyd-Steinberg error diffusion when deciding whether to place a shape at each grid cell. This gives the pattern an organic, slightly irregular feel that works much better aesthetically than a strict probabilistic grid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Coverage calculation&lt;/strong&gt; — The tool shows the exact percentage of material that will be removed. This is critical for structural decisions: if you&apos;re cutting a gate panel, you need to know how much strength you&apos;re losing. If you&apos;re designing a speaker grille, you need to know how much sound will pass through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Export formats&lt;/strong&gt; — PDF for print and sharing, DXF for direct import into CAD/CAM software. Both are vector formats, so the shop gets clean paths at any scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dark mode&lt;/strong&gt; — Because I built this at night, and staring at a white canvas hurts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;How It Works&lt;/h2&gt;
&lt;p&gt;The engine is a pure JavaScript function that takes a settings object and returns an array of shape descriptors. Each shape has a position, size, type, and optional properties like line length or corner radius.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Simplified idea&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; shapes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generatePattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;shapeType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;circle&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;shapeSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;spacing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;gradientType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;topToBottom&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The grid is computed from the canvas size, margins, shape size, and spacing. For each cell, the engine calculates a target density based on the gradient, applies the dithering algorithm, and if the cell passes, it places a shape. Lines are handled differently — they are placed in rows or columns with random lengths within a min/max range, skipping cells based on the same density logic.&lt;/p&gt;
&lt;p&gt;The SVG preview renders these shapes directly. The PDF exporter uses &lt;code class=&quot;language-text&quot;&gt;svg2pdf.js&lt;/code&gt; to convert the live SVG DOM into a vector PDF with exact page dimensions. The DXF exporter writes raw DXF entities (LWPOLYLINE for lines, CIRCLE for circles) so the file opens cleanly in AutoCAD, LibreCAD, or any CAM software.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;The real win isn&apos;t the code — it&apos;s the iteration speed. I can sit with my laptop, adjust the density, see the pattern change, and know immediately whether it will look right on a 2-meter gate. I can check the coverage percentage and make sure I&apos;m not cutting away so much metal that the panel loses rigidity. I can export a DXF, email it to the shop, and have a quote the same day.&lt;/p&gt;
&lt;p&gt;Before writing this tool, I tried drawing patterns in Figma. It works, but it&apos;s tedious. Every change requires manual selection, nudging, and copy-pasting. The generator removes that friction entirely. The pattern is deterministic enough to look intentional, but random enough to feel organic.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Gate&lt;/h2&gt;
&lt;p&gt;The gate I ended up with uses vertical lines, 30 mm thick, with a top-to-bottom density gradient. The lines are 100–400 mm long, placed in columns with random gaps. The top is sparse — almost open — letting light through. The bottom is dense, giving privacy and a solid visual anchor. The DXF went straight to the laser shop.&lt;/p&gt;
&lt;p&gt;The total cutout area is about 35% of the sheet. That felt right: enough transparency to keep the space from feeling like a wall, enough material to keep it strong.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Try It&lt;/h2&gt;
&lt;p&gt;You can use the generator here: &lt;a href=&quot;/design-pattern-generator&quot;&gt;&lt;strong&gt;/design-pattern-generator&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s free, runs in your browser, and exports PDF and DXF. If you build something with it — a gate, a grille, a lamp shade, a room divider — I&apos;d love to see it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building Semantic Search Over a Knowledge Base]]></title><description><![CDATA[Semantic search embeds query and documents into a shared vector space and retrieves by similarity, so paraphrase and synonymy are handled by construction. This is what the rest of this post is about.]]></description><link>https://mihaiserban.dev/blog/building-semantic-search-over-a-knowledge-base/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/building-semantic-search-over-a-knowledge-base/</guid><pubDate>Wed, 06 May 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Keyword search breaks whenever users and authors describe the same concept differently. A user searching &lt;em&gt;&quot;how do I take money out of my account&quot;&lt;/em&gt; may never find an article titled &lt;em&gt;&quot;Withdrawal Methods&quot;&lt;/em&gt;, even though it&apos;s exactly what they want.&lt;/p&gt;
&lt;p&gt;Semantic search fixes this by ranking documents based on meaning instead of token overlap. This post walks through a production semantic-search architecture for knowledge bases: retrieval, reranking, chunking, ingestion, caching, evaluation, and the operational tradeoffs that matter once you go past a prototype.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Lexical, Semantic, and Hybrid Search&lt;/h2&gt;
&lt;p&gt;Before going deeper, it&apos;s worth being precise about the three retrieval approaches you&apos;ll see compared in any serious search project, because they fail in different ways and the right choice depends on what your corpus and your users look like.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lexical search&lt;/strong&gt; is the classical approach: tokenize the query and the documents, then score on token overlap with something like BM25. It&apos;s cheap, interpretable, requires no model, and is excellent at &lt;em&gt;exact-match&lt;/em&gt; recall — product codes, error codes, version strings, proper nouns. If a user pastes &lt;code class=&quot;language-text&quot;&gt;ERR_QUOTA_EXCEEDED&lt;/code&gt; into the search box, lexical search will find it in milliseconds with no training data. Its failure mode is the one in the intro: when the user and the author describe the same concept with different words, token overlap goes to zero and recall collapses. &lt;em&gt;&quot;Take money out&quot;&lt;/em&gt; and &lt;em&gt;&quot;Withdrawal Methods&quot;&lt;/em&gt; share no tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Semantic search&lt;/strong&gt; embeds query and documents into a shared vector space and retrieves by similarity, so paraphrase and synonymy are handled by construction. This is what the rest of this post is about. Its weakness is the mirror image of lexical&apos;s strength: rare tokens the embedding model never saw during training have nowhere meaningful to live in vector space, so they get smeared into nearby concepts. SKUs, internal API names, version numbers, and obscure proper nouns retrieve poorly even when an exact copy of the string exists in the corpus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hybrid search&lt;/strong&gt; runs both retrievers in parallel and fuses the results, usually with Reciprocal Rank Fusion (RRF) or a weighted blend of the two normalized score lists. You get exact-match recall on rare tokens &lt;em&gt;and&lt;/em&gt; paraphrase recall on prose, at the cost of running two retrievers and tuning a fusion step.&lt;/p&gt;
&lt;p&gt;The selection rule, in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pure lexical&lt;/strong&gt; — small or mostly-structured corpora, search-by-identifier workloads, environments where you don&apos;t want the operational weight of embeddings at all.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pure semantic&lt;/strong&gt; — prose-heavy knowledge bases, FAQ-style queries, traffic dominated by paraphrase. This is the assumption the rest of this post runs with.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid&lt;/strong&gt; — mixed corpora with both prose and rare-token content: developer docs that include API names, e-commerce catalogs with SKUs, support content where users will happily paste error codes alongside natural-language questions. When you don&apos;t know in advance which mode your users will lean on, hybrid is the safe default.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The rest of this post focuses on the semantic path because that&apos;s the harder system to get right — but the architecture below extends to hybrid cleanly: lexical retrieval becomes a second candidate source feeding the same reranker, and everything downstream stays the same.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Core Idea: Two-Stage Retrieval&lt;/h2&gt;
&lt;p&gt;Modern semantic search almost universally uses two stages, because no single model is both fast enough to score every document &lt;em&gt;and&lt;/em&gt; accurate enough to rank the top results well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Query ──▶ Bi-encoder ──▶ Vector DB ──▶ Top 30 ──▶ Cross-encoder ──▶ Top N
         (fast, broad)   (recall)              (slow, precise)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Stage 1 — Retrieval (bi-encoder).&lt;/strong&gt; A bi-encoder embeds the query and every document independently into the same vector space. At query time you only embed the query; document vectors are precomputed. You then ask a vector database for the &lt;em&gt;k&lt;/em&gt; nearest neighbours by cosine similarity. This is fast because it&apos;s a single forward pass plus an ANN lookup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage 2 — Reranking (cross-encoder).&lt;/strong&gt; A cross-encoder takes &lt;code class=&quot;language-text&quot;&gt;(query, document)&lt;/code&gt; pairs together and produces a relevance score. A bi-encoder compresses query and document independently into fixed vectors, so token-level nuance is lost; a cross-encoder attends over both texts jointly, which is what lets it learn that &lt;em&gt;&quot;take money out&quot;&lt;/em&gt; and &lt;em&gt;&quot;withdrawal&quot;&lt;/em&gt; are the same intent. The cost is that it can&apos;t be precomputed, and it&apos;s an order of magnitude slower per document — so you only run it over the top 30 candidates from stage 1.&lt;/p&gt;
&lt;p&gt;The split matters: you scale recall with the bi-encoder and precision with the cross-encoder, and you keep latency bounded.&lt;/p&gt;
&lt;p&gt;To make this concrete, take the query &lt;em&gt;&quot;how do I take money out of my account&quot;&lt;/em&gt; through the pipeline:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Query: &quot;how do I take money out of my account&quot;

Stage 1 — bi-encoder retrieves top candidates:
  1. &quot;Withdrawal Methods&quot;
  2. &quot;Bank Transfer Limits&quot;
  3. &quot;ATM Cash Withdrawal&quot;
  4. &quot;Account Closure&quot;
  ... (30 total)

Stage 2 — cross-encoder reranks:
  1. &quot;Withdrawal Methods&quot;        (0.94)
  2. &quot;ATM Cash Withdrawal&quot;       (0.81)
  3. &quot;Bank Transfer Limits&quot;      (0.62)
  ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The bi-encoder gets the right cluster of articles into the candidate set; the cross-encoder figures out that &lt;em&gt;&quot;Withdrawal Methods&quot;&lt;/em&gt; is what the user actually wanted, even though &lt;em&gt;&quot;Bank Transfer Limits&quot;&lt;/em&gt; shares more vocabulary with everyday banking language.&lt;/p&gt;
&lt;h3&gt;Choosing models&lt;/h3&gt;
&lt;p&gt;For most production workloads you don&apos;t need state-of-the-art research models. CPU-friendly small models are usually enough:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bi-encoder:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;intfloat/multilingual-e5-small&lt;/code&gt; — 384 dimensions, multilingual, ~50–100ms per query on CPU.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-encoder:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;cross-encoder/ms-marco-TinyBERT-L2-v2&lt;/code&gt; — small enough that scoring 30 candidates takes ~300–800ms on CPU.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These run comfortably on commodity hardware. GPU inference is rarely justified at this scale: model size and batch sizes are small, CPU latency is well under your typical p99 budget, and CPU instances are dramatically cheaper.&lt;/p&gt;
&lt;p&gt;Always &lt;strong&gt;L2-normalize&lt;/strong&gt; bi-encoder output. Cosine similarity on normalized vectors becomes a dot product, which most vector indexes optimize for, and it makes scores comparable across queries.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Vector Storage: Why PostgreSQL + pgvector&lt;/h2&gt;
&lt;p&gt;The reflexive choice is a dedicated vector database (Pinecone, Weaviate, Qdrant, Milvus). For a lot of systems that&apos;s overkill.&lt;/p&gt;
&lt;p&gt;If you already run PostgreSQL, the &lt;code class=&quot;language-text&quot;&gt;pgvector&lt;/code&gt; extension gives you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vectors as a column type alongside your normal relational data.&lt;/li&gt;
&lt;li&gt;HNSW and IVFFlat indexes with cosine, L2, and inner-product distance.&lt;/li&gt;
&lt;li&gt;ACID transactions, joins, filtering by &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; clauses, real backups.&lt;/li&gt;
&lt;li&gt;A boring, well-understood operational profile.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A typical schema looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; EXTENSION vector&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; embeddings &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  id           BIGSERIAL &lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  article_id   &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  tenant       &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt;     &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  chunk_type   &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;token comment&quot;&gt;-- &apos;title&apos; | &apos;summary&apos; | &apos;paragraph&apos;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt;         &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  embedding    VECTOR&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;384&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  metadata     JSONB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  created_at   TIMESTAMPTZ &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NOW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; embeddings
  &lt;span class=&quot;token keyword&quot;&gt;USING&lt;/span&gt; hnsw &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;embedding vector_cosine_ops&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ef_construction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; embeddings &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tenant&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Multi-tenancy is best handled by a &lt;code class=&quot;language-text&quot;&gt;tenant&lt;/code&gt; column rather than separate databases. One HNSW index serves everyone, you filter at query time, and you don&apos;t have to manage N migration pipelines.&lt;/p&gt;
&lt;p&gt;The query at retrieval time:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; article_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chunk_type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;embedding &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&gt;&lt;/span&gt; $query_vector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; similarity
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; embeddings
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; tenant &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $tenant
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $lang
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; embedding &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&gt;&lt;/span&gt; $query_vector
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;&amp;lt;=&gt;&lt;/code&gt; operator is cosine distance under &lt;code class=&quot;language-text&quot;&gt;vector_cosine_ops&lt;/code&gt;. Using it in &lt;code class=&quot;language-text&quot;&gt;ORDER BY&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;LIMIT&lt;/code&gt; is what triggers HNSW; selecting it as a column for the score does not.&lt;/p&gt;
&lt;p&gt;Dedicated vector databases become the right call when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vector counts reach tens or hundreds of millions.&lt;/li&gt;
&lt;li&gt;Filtered ANN queries dominate and pgvector&apos;s planner struggles to combine HNSW with &lt;code class=&quot;language-text&quot;&gt;WHERE&lt;/code&gt; clauses efficiently.&lt;/li&gt;
&lt;li&gt;You need multi-region replication of the index itself, not just the source data.&lt;/li&gt;
&lt;li&gt;Search becomes its own platform with a dedicated team behind it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before that point, pgvector is usually operationally simpler — one database, one backup story, one set of credentials.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Chunking: The Underrated Decision&lt;/h2&gt;
&lt;p&gt;A 2,000-word article embedded into a single vector is a bad vector. The signal from the introduction gets diluted by every paragraph that follows, and search recall drops on long-form content.&lt;/p&gt;
&lt;p&gt;The fix is to embed chunks. For a knowledge base, the natural chunks are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Title&lt;/strong&gt; — short, dense, very high signal for direct intent matches.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Summary&lt;/strong&gt; — slightly longer, captures the &quot;what is this about&quot; framing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paragraphs&lt;/strong&gt; — individual sections that may answer specific sub-questions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each chunk becomes its own row in the embeddings table, all pointing back to the same &lt;code class=&quot;language-text&quot;&gt;article_id&lt;/code&gt;. At search time you retrieve chunks, then deduplicate to articles by taking the best-scoring chunk per article — weighted by chunk type so a title match counts for more than a paragraph match.&lt;/p&gt;
&lt;p&gt;Why max-with-weight rather than sum? Summing rewards long articles for having many paragraphs, which is exactly the opposite of what you want. The weight prevents a paragraph match from beating a title match on the same query, since title matches usually mean the article is &lt;em&gt;about&lt;/em&gt; the query, not just &lt;em&gt;mentions&lt;/em&gt; it. Reasonable starting weights are roughly 1.0 for titles, ~0.85 for summaries, and ~0.7 for paragraphs — tune from there against your evaluation set.&lt;/p&gt;
&lt;p&gt;A toy example makes the effect obvious. Query: &lt;em&gt;&quot;reset MFA&quot;&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Article A — &quot;Reset Multi-Factor Authentication&quot;
  title    similarity = 0.91
  paragraph similarity = 0.74

Article B — &quot;Account Security Best Practices&quot;
  title    similarity = 0.70
  paragraph similarity = 0.82   (mentions MFA reset in passing)

Weighted max:
  A = 0.91 × 1.0  = 0.91
  B = 0.82 × 0.7  = 0.57&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Article A wins because the title strongly matches the intent, even though Article B has a higher raw paragraph similarity. Without weighting, B would beat A — and the user would land on the wrong article.&lt;/p&gt;
&lt;p&gt;For arbitrary text (long-form documentation, PDFs, scraped pages) you don&apos;t have natural title/summary/paragraph boundaries. There you need fixed-size chunking with a small overlap (~10–20%) so a concept that straddles a chunk boundary still appears in at least one chunk. For structured KB articles, the natural sections are almost always cleaner than fixed token windows — don&apos;t cargo-cult overlap into a domain that doesn&apos;t need it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Ingestion: Make It Asynchronous&lt;/h2&gt;
&lt;p&gt;A naive ingestion endpoint embeds and writes synchronously inside the HTTP request. This works until:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An author bulk-uploads 5,000 articles and your API times out.&lt;/li&gt;
&lt;li&gt;The embedding service goes briefly unhealthy and you lose writes.&lt;/li&gt;
&lt;li&gt;A burst of updates pegs CPU and search latency goes through the roof.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fix is two-phase ingestion with a queue between them:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;                 ┌─────────────────┐
HTTP POST  ──▶  │ Ingestion API   │ ──▶ FIFO Queue ──▶ Worker pool
                 │ (validate +     │                    (chunk → embed
                 │  enqueue)       │                     → store →
                 └─────────────────┘                     invalidate cache)
                         │
                  HTTP 202 Accepted&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Phase A — synchronous.&lt;/strong&gt; Validate the payload (required fields, schema, size limits), enqueue a message, return &lt;code class=&quot;language-text&quot;&gt;202 Accepted&lt;/code&gt; with a tracking ID. This should take a few milliseconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase B — asynchronous.&lt;/strong&gt; A worker pool polls the queue, processes messages with long-polling and parallelism, and writes results to PostgreSQL.&lt;/p&gt;
&lt;p&gt;A FIFO queue (e.g. SQS FIFO) buys you ordering guarantees per message group — important for &lt;code class=&quot;language-text&quot;&gt;CREATE&lt;/code&gt; followed by &lt;code class=&quot;language-text&quot;&gt;UPDATE&lt;/code&gt; on the same article landing in the right order — plus a dead-letter queue for poison messages. Worker concurrency can scale on queue depth independently of HTTP traffic.&lt;/p&gt;
&lt;p&gt;Message actions worth supporting from day one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;CREATE&lt;/code&gt; — embed and insert chunks.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;UPDATE&lt;/code&gt; — delete existing chunks for the article, then re-embed (it&apos;s simpler and safer than diffing).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; — remove all chunks for an article.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DELETE_ALL&lt;/code&gt; — clear a tenant. Useful for re-indexing.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Caching: Search-Result Cache, Not Embedding Cache&lt;/h2&gt;
&lt;p&gt;The temptation is to cache embeddings. Don&apos;t bother — they&apos;re already in PostgreSQL, and recomputing them is rare. The high-value cache is at the &lt;strong&gt;search-result&lt;/strong&gt; layer, where a popular query may be issued thousands of times per minute with the same exact answer.&lt;/p&gt;
&lt;p&gt;Cache-aside is the right pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Search API receives a query, builds a cache key, checks Redis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hit&lt;/strong&gt;: return immediately. Total latency is a single Redis round-trip.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Miss&lt;/strong&gt;: run the full pipeline (embed → retrieve → rerank), store the result with a TTL, return.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The cache key should encode every dimension that affects the result:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;search:{tenant}:{lang}:{version}:{filter_hash}:{query_hash}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;tenant&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;lang&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;version&lt;/code&gt; are namespaces — they let you invalidate by prefix.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;filter_hash&lt;/code&gt; covers any structured filters (categories, product IDs).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;query_hash&lt;/code&gt; is a hash of the normalized query string (lowercased, trimmed, collapsed whitespace).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A 5-minute TTL is a good starting point. Long enough to absorb traffic spikes from trending queries; short enough that stale results from edits self-heal even if invalidation fails.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Invalidation matters more than TTL.&lt;/strong&gt; When the ingestion worker writes a new article, it should delete cache entries that could now be wrong. The structured key prefix makes this a single pattern delete:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;DEL search:{tenant}:*&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You&apos;re trading freshness against efficiency. Per-tenant invalidation is usually the right granularity — finer-grained invalidation is hard to get right and rarely worth the complexity.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Service Layout: Why Microservices Help Here&lt;/h2&gt;
&lt;p&gt;The naive single-service deployment puts API handling, embedding, reranking, and ingestion in one process. This works on day one. It fails on day 100 because the workloads scale on completely different signals.&lt;/p&gt;
&lt;p&gt;A four-service split — search API, embedding service, reranker service, ingestion worker — lets each scale on what actually drives its load:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Bottleneck&lt;/th&gt;
&lt;th&gt;Scale on&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Search API&lt;/td&gt;
&lt;td&gt;HTTP/orchestration&lt;/td&gt;
&lt;td&gt;Request rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embedding&lt;/td&gt;
&lt;td&gt;CPU (one inference/req)&lt;/td&gt;
&lt;td&gt;CPU %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reranker&lt;/td&gt;
&lt;td&gt;CPU (~30 inferences/req)&lt;/td&gt;
&lt;td&gt;CPU %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ingestion&lt;/td&gt;
&lt;td&gt;Backlog&lt;/td&gt;
&lt;td&gt;Queue depth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The reranker is the interesting one. It receives 30 candidates &lt;em&gt;per single search&lt;/em&gt; and runs the cross-encoder over all of them in one batch. That&apos;s roughly 6–10× the CPU work of the embedding service per request, so under a 5× traffic spike the reranker may need 8× capacity while the embedding service only needs 2×. If everything scaled together on a single signal, the reranker would be the bottleneck and your tail latencies would blow up.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Evaluation: The System You Actually Tune Against&lt;/h2&gt;
&lt;p&gt;This is the section that separates demos from production systems, and it&apos;s the one most teams skip until they regret it. Without an evaluation set, every change you make to the pipeline — a new model, a different chunking strategy, tweaked weights — becomes anecdotal. &lt;em&gt;&quot;It feels better&quot;&lt;/em&gt; is not a metric.&lt;/p&gt;
&lt;p&gt;Build a labeled set early. It doesn&apos;t need to be huge; 50–200 &lt;code class=&quot;language-text&quot;&gt;(query, relevant_article_ids)&lt;/code&gt; pairs are enough to start catching regressions:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Expected article&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&quot;take money out&quot;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;withdrawal-methods&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;change my password&quot;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;reset-password&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;cancel subscription&quot;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;billing-cancellation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;two factor reset&quot;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;reset-mfa&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Then measure on every change:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Recall@30&lt;/strong&gt; — did the relevant article appear in the top 30 candidates? This isolates the bi-encoder + index.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MRR / nDCG@10&lt;/strong&gt; — was it ranked highly in the final result? This isolates the reranker and the score-aggregation logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency p50/p95&lt;/strong&gt; — broken down by stage so you can see which subsystem is moving.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache hit rate&lt;/strong&gt; — telling you whether your TTL and key design match real query patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The diagnostic value is what matters more than any individual number:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;High Recall@30 but low MRR → ranking problem. Look at the reranker, scoring weights, or chunk selection.&lt;/li&gt;
&lt;li&gt;Low Recall@30 → retrieval problem. Look at chunking, the bi-encoder, or filters that are too aggressive.&lt;/li&gt;
&lt;li&gt;Both fine on the eval set but users complain → your eval set doesn&apos;t represent real queries. Sample from logs.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Putting Numbers on It&lt;/h2&gt;
&lt;p&gt;A reasonable target for a system like this on commodity hardware:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cached search&lt;/strong&gt; (Redis hit): under 50ms end-to-end.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fresh search&lt;/strong&gt; (full pipeline): under 400ms p95.
&lt;ul&gt;
&lt;li&gt;Embedding: 50–100ms&lt;/li&gt;
&lt;li&gt;Vector retrieval (HNSW over millions of rows): 20–50ms&lt;/li&gt;
&lt;li&gt;Reranking 30 candidates: 300–500ms&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingestion&lt;/strong&gt;: 2–5 seconds per article end-to-end, fully async. A worker pool of 4–8 processes handles tens of thousands of articles per hour.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These numbers are entirely CPU-only. The whole stack — Postgres, Redis, queue, four services on Kubernetes — runs comfortably for a few hundred dollars a month at moderate scale, which is far cheaper than most managed vector-DB offerings or GPU-backed inference setups. Worth being explicit: this architecture uses small transformer encoders (bi-encoder for retrieval, cross-encoder for reranking), not an LLM. There&apos;s no generative step in the request path, which is exactly why latency and cost stay flat.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;A Mental Model to Keep&lt;/h2&gt;
&lt;p&gt;Semantic search at production scale is mostly about recognizing that the system has three axes — &lt;strong&gt;recall, precision, freshness&lt;/strong&gt; — and each subsystem moves only one of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The bi-encoder and HNSW index drive &lt;strong&gt;recall&lt;/strong&gt;. Get the right candidates into the top 30.&lt;/li&gt;
&lt;li&gt;The cross-encoder drives &lt;strong&gt;precision&lt;/strong&gt;. Order them correctly.&lt;/li&gt;
&lt;li&gt;The ingestion pipeline and cache invalidation drive &lt;strong&gt;freshness&lt;/strong&gt;. Show what&apos;s true now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most search-quality problems are misdiagnosed because someone tries to fix a precision problem with a recall tool, or vice versa. If the right answer isn&apos;t in the top 30 candidates, no reranker can save you. If it&apos;s there but ranked 8th, no amount of bi-encoder tuning helps. Look at where in the pipeline the answer falls out, then fix that stage.&lt;/p&gt;
&lt;p&gt;The surprising part of running production semantic search is that the hard problems are rarely model problems. They&apos;re systems problems: chunking, evaluation, cache invalidation, ingestion ordering, latency under load, and knowing whether a given failure is about recall or ranking.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cloning a Windows drive from Mac OS X using dd (Disk Destroyer)]]></title><description><![CDATA[Decided to upgrade upgrade my Windows drive on my dual boot Hackintosh. Here's how to do it from your Mac OS X drive using dd (Disk Destroyer).]]></description><link>https://mihaiserban.dev/blog/cloning-windows-drive-from-mac-os-x-using-dd-disk-destroyer/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/cloning-windows-drive-from-mac-os-x-using-dd-disk-destroyer/</guid><pubDate>Thu, 07 Jan 2021 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Decided to upgrade upgrade my Windows drive on my dual boot Hackintosh. Here&apos;s how to do it from your Mac OS X drive.&lt;/p&gt;
&lt;p&gt;Open your terminal. Run &lt;code class=&quot;language-text&quot;&gt;diskutil list&lt;/code&gt; to get the disk identifier you want to migrate.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;diskutil list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In my case the Windows drive is &lt;code class=&quot;language-text&quot;&gt;/dev/disk1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s clone the drive using &lt;code class=&quot;language-text&quot;&gt;dd&lt;/code&gt;. Set input file to the drive you want to clone, make sure to add &lt;code class=&quot;language-text&quot;&gt;r&lt;/code&gt;, makes it faster. Set output file to a &lt;code class=&quot;language-text&quot;&gt;.iso&lt;/code&gt; location of your choice.
This operation will take a couple of hours.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;sudo dd if=/dev/rdisk1 of=/Users/mitzuuuu/Desktop/windows.iso&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Install your empty drive and run &lt;code class=&quot;language-text&quot;&gt;diskutil list&lt;/code&gt; again to get the identifier of the new drive, &lt;code class=&quot;language-text&quot;&gt;/dev/disk0&lt;/code&gt; in my case.&lt;/p&gt;
&lt;p&gt;Write the .iso you&apos;ve previously generated to the new installed drive.
This operation will take a couple of hours.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;sudo dd if=/Users/mitzuuuu/Desktop/windows.iso of=/dev/disk0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After the migration is done, you need to take care of the &lt;code class=&quot;language-text&quot;&gt;Unnalocated space&lt;/code&gt;. Boot into the Windows partition. Create a new partition, and finally merge it with the master windows partition if you want to.&lt;/p&gt;
&lt;p&gt;Success!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to handle AWS SES bounces and complaints]]></title><description><![CDATA[If you’re thinking of implementing AWS Simple Email Service for your product, you might find out that you need a flow to handle email…]]></description><link>https://mihaiserban.dev/blog/how-to-handle-aws-ses-bounces-and-complaints/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/how-to-handle-aws-ses-bounces-and-complaints/</guid><category><![CDATA[AWS]]></category><pubDate>Thu, 01 Nov 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’re thinking of implementing AWS Simple Email Service for your product, you might find out that you need a flow to handle email bounces and complaints before AWS approves your service quota increase and take your SES account out of sandbox mode.&lt;/p&gt;
&lt;p&gt;This requirement assures SES maintains a high reputation for only delivering mail people want and thereby maintaining a high deliverability for legitimate mail.&lt;/p&gt;
&lt;p&gt;What exactly are email bounces and complaints?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bounce&lt;/strong&gt; email happens when an is returned to the sender because it cannot be delivered for some reason.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Complaints&lt;/strong&gt; are reports made by email recipients against emails they don’t want in their inbox. Mark as SPAM for example triggers such a report. Email Service Providers (ESPs), have what is called a “feedback loop” with all of the major Internet Service Provides (ISPs).&lt;/p&gt;
&lt;p&gt;In case your API receives a Bounce/Complaint you should take steps to make sure it doesn’t happen again. Easiest way is to not send emails to that user, unless he agrees to receive it.&lt;/p&gt;
&lt;h4&gt;Overview of the sending process&lt;/h4&gt;
&lt;p&gt;The following figure shows the process of sending an email via &lt;a href=&quot;http://aws.amazon.com/ses/&quot;&gt;AWS SES&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_pEz2kdAeiT6ljb32F6J9fw.png&quot; alt=&quot;AWS SES diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;If the sender request to SES succeds then it can expect one of the following outcomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;success&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bounce&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;complaint&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Overview of handling of bounce/complaints&lt;/h4&gt;
&lt;p&gt;The following figure shows the process of handling bounce/complaints by using &lt;a href=&quot;https://aws.amazon.com/sns&quot;&gt;AWS SNS&lt;/a&gt; service.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_-eRHvZt-9R_R_7f6f0yCvw.png&quot; alt=&quot;AWS SES flow diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;Bounce and complaint notifications are available by email or through Amazon Simple Notification Service (Amazon SNS). By default, these notifications are sent to you via email by a feature called &lt;em&gt;email feedback forwarding&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;1. Setup AWS SNS topics for bounce and complaints&lt;/h4&gt;
&lt;p&gt;Create the following topics in &lt;a href=&quot;https://docs.aws.amazon.com/sns/latest/dg/sns-http-https-endpoint-as-subscriber.html#SendMessageToHttp.prepare&quot;&gt;AWS SNS&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ses-bounces-topic-prod&lt;/li&gt;
&lt;li&gt;ses-complaints-topic-prod&lt;/li&gt;
&lt;li&gt;ses-deliveries-topic-prod (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_JZ8CVlRWquIv20SKnUIjHg.png&quot; alt=&quot;AWS SES create topic&quot;&gt;&lt;/p&gt;
&lt;p&gt;After creating each topic, you’ll receive a identity id is called &lt;strong&gt;ARN,&lt;/strong&gt; which we need in the next step of creating a SNS subscription.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_sWjh8Qxn-o5wyqvI5Ipe3Q.png&quot; alt=&quot;AWS SES topics&quot;&gt;&lt;/p&gt;
&lt;p&gt;Head to &lt;strong&gt;SNS Subscriptions&lt;/strong&gt; and create a SNS subscription for the bounce and complaint topics you’ve previously created.&lt;/p&gt;
&lt;p&gt;This is where we need to specify a Endpoint where we’ll receive notifications from each topic. Endpoint must be a &lt;strong&gt;POST&lt;/strong&gt; method on your backend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1__Bnw9nIcRwC4LjH9hd3Mow.png&quot; alt=&quot;AWS SES create subscription&quot;&gt;&lt;/p&gt;
&lt;p&gt;Each Subscription needs to be confirmed, after creation they are in a &lt;strong&gt;PendingConfirmation&lt;/strong&gt; state.&lt;/p&gt;
&lt;p&gt;To confirm our subscription, we need to implement the endpoints in our backend, and call Request confirmations from the SNS dashboard.&lt;/p&gt;
&lt;p&gt;In the body received on our server we’ll find the SubscribeURL or Token which we can use to confirm.&lt;/p&gt;
&lt;p&gt;Call sns.confirmSubscription() with the Token or copy pasting SubscribeURL into SNS Dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_T_bSIOjMnaOaHs9PwEevnw.png&quot; alt=&quot;AWS SES confirm subscription url&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I’ve provided the code to subscribe and confirm each endpoint on&lt;/strong&gt; &lt;a href=&quot;https://gist.github.com/mihaiserban/8a03fd28e54cac8856dbdfebd95bd7b3&quot;&gt;&lt;strong&gt;Github&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP: Make sure your IAM User has access to SNS&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;2. Configure SES to publish notifications to each created SNS topic&lt;/h4&gt;
&lt;p&gt;Go to the SES Managment Console -&gt; Email Addresses -&gt; Select email address, and open Notifications.&lt;/p&gt;
&lt;p&gt;Edit configuration and select the SNS topic for each type of notification.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_k4fHq6CgGYUeXZjNmyXOLA.png&quot; alt=&quot;AWS SES notifications configuration&quot;&gt;&lt;/p&gt;
&lt;h4&gt;3. Testing using &lt;a href=&quot;https://aws.amazon.com/blogs/aws/mailbox-simulator-for-the-amazon-simple-email-service/&quot;&gt;AWS Mailbox Simulator&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The AWS mailbox simulator can be found in SES Managment Console and provides a way to test the way your implementation handles scenarios like bounces and complaints.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_Pydm3yb5aGuuRerVw6mxcQ.png&quot; alt=&quot;AWS SES test email&quot;&gt;&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:success@simulator.amazonses.com&quot;&gt;success@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will be treated as delivered successfully.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:bounce@simulator.amazonses.com&quot;&gt;bounce@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will be rejected with an SMTP 550 (“Unknown User”) response code. Amazon SES will send you a bounce notification by email or by SNS notification.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:ooto@simulator.amazonses.com&quot;&gt;ooto@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will be treated as delivered successfully.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:complaint@simulator.amazonses.com&quot;&gt;complaint@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will simulate the case in which the recipient clicks &lt;strong&gt;Mark as Spam&lt;/strong&gt; within their email application and the ISP sends a complaint response to Amazon SES.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:blacklist@simulator.amazonses.com&quot;&gt;blacklist@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will cause Amazon SES to block the send attempt and return a &lt;a href=&quot;http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/Troubleshooting.MessageRejected.html&quot;&gt;MessageRejected&lt;/a&gt; error containing an “Address Blacklisted” error message.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to fix broken images in React.]]></title><description><![CDATA[In one of my recent projects we encountered many images which were missing from our S3 bucket. When I see something like this it just makes me sick 😫.]]></description><link>https://mihaiserban.dev/blog/how-to-fix-broken-images-in-react/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/how-to-fix-broken-images-in-react/</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><pubDate>Wed, 24 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_xIlLqtM0dSTY3KZ03zckMg.png&quot; alt=&quot;Missing image example&quot;&gt;&lt;/p&gt;
&lt;p&gt;In one of my recent projects we encountered many images which were missing from our S3 bucket. When I see something like this it just makes me sick 😫.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; provides us with two events, &lt;code class=&quot;language-text&quot;&gt;onLoad&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;onError&lt;/code&gt;. We can use these two keep track of the status of the image.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;onError&lt;/code&gt; is called when our image has failed to load, and we can set &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; to our preferred fallback image.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;onLoad&lt;/code&gt; is called when our image loaded successfully, nothing for us to do here.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import React from &quot;react&quot;;  
import PropTypes from &quot;prop-types&quot;;  

class Image extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = { src: props.src };  
  }  

  componentWillReceiveProps(nextProps) {  
    if (this.props.src !== nextProps.src) {  
      this.setState({  
        src: props.src  
      });  
    }  
  }  

  handleImageLoad() {  
    console.log(&quot;image loaded&quot;, this.state.src);  
  }  

  handleImageError() {  
    console.log(&quot;image failed loaded&quot;, this.state.src);  
    if (this.props.placeholder) {  
      console.log(&quot;try load placeholder&quot;);  
      this.setState({ src: this.props.placeholder });  
    }  
  }  

  handleOnContextMenu(e) {  
    console.log(&quot;handleOnContextMenu&quot;);  
    if (this.props.disableContextMenu) {  
      e.preventDefault();  
    }  
  }  

  render() {  
    const { src, placeholder, disableContextMenu, ...other } = this.props; // es7  

    return (  
      &amp;lt;img  
        src={this.state.src}  
        onLoad={this.handleImageLoad.bind(this)}  
        onError={this.handleImageError.bind(this)}  
        {...other}  
        onContextMenu={this.handleOnContextMenu.bind(this)}  
      /&gt;  
    );  
  }  
}  

Image.defaultProps = {  
  src: &quot;&quot;,  
  placeholder: &quot;&quot;,  
  disableContextMenu: false  
};  

Image.propTypes = {  
  src: PropTypes.string.isRequired,  
  placeholder: PropTypes.string,  
  disableContextMenu: PropTypes.bool  
};  

export default Image;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Also available as a &lt;a href=&quot;https://gist.github.com/mihaiserban/751a84df361178db387e130d0c07693e&quot;&gt;Gist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Arrow Functions]]></title><description><![CDATA[Arrows are a function shorthand using the => syntax. Arrow functions allow you to preserve the lexical value of this.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-arrow-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-arrow-functions/</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ES6]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_zxHFGY9JpcDDsB5vAcNMtg.png&quot; alt=&quot;ES6 Arrow functions&quot;&gt;&lt;/p&gt;
&lt;p&gt;Arrows are a function shorthand using the &lt;code class=&quot;language-text&quot;&gt;=&gt;&lt;/code&gt; syntax. Arrow functions allow you to preserve the lexical value of &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Take the example below where we have a nested function, in which we would like to preserve the context of &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; from its lexical scope:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Person(name) {  
    this.name = name;  
}  

Person.prototype.prefixName = function (arr) {  
    return arr.map(function (character) {  
        return this.name + character; // Cannot read property &apos;name&apos; of undefined  
    });  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using Arrow Functions, the lexical value of &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; isn&apos;t shadowed and we can re-write the above as shown:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Person(name) {  
    this.name = name;  
}  

Person.prototype.prefixName = function (arr) {  
    return arr.map(character =&gt; this.name + character);  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If an arrow is inside another function, it shares the &lt;code class=&quot;language-text&quot;&gt;arguments&lt;/code&gt; variable of its parent function. Example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// Lexical arguments  
function square() {  
  let example = () =&gt; {  
    let numbers = [];  
    for (let number of arguments) {  
      numbers.push(number * number);  
    }  

    return numbers;  
  };  

  return example();  
}  

square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Async Await]]></title><description><![CDATA[Async/Await is a new way to write asynchronous code. Previous options for asynchronous code are callbacks and promises.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-async-await/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-async-await/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_U5MZoXlTOdyxlwf7XBqcHw.png&quot; alt=&quot;ES6 Async/Await&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Async/await is a new way to write asynchronous code. Previous options for asynchronous code are callbacks and promises.&lt;/li&gt;
&lt;li&gt;Async/await is built on top of promises. It cannot be used with plain callbacks or node callbacks.&lt;/li&gt;
&lt;li&gt;Async/await makes asynchronous code look and behave a little more like synchronous code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt; may only be used in functions marked with the &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt; keyword. It works similarly to generators, suspending execution in your context until the promise settles. If the awaited expression isn’t a promise, its casted into a promise.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;async await&lt;/code&gt; allows us to perform the same thing we accomplished using Generators and Promises with less effort:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var request = require(&apos;request&apos;);  

function getJSON(url) {  
  return new Promise(function(resolve, reject) {  
    request(url, function(error, response, body) {  
      resolve(body);  
    });  
  });  
}  

async function main() {  
  var data = await getJSON();  
  console.log(data);  
}  

main();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Under the hood, it performs similarly to &lt;a href=&quot;https://medium.com/@serbanmihai/javascript-es6-cheatsheet-generators-997cc977f7f1&quot;&gt;Generators&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Classes]]></title><description><![CDATA[Prior to ES6, we implemented Classes by creating a constructor function and  adding properties by extending the prototype]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-classes/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-classes/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_zdv7_BKFCrpmGwfPgu6j3A.png&quot; alt=&quot;ES6 Classes&quot;&gt;&lt;/p&gt;
&lt;p&gt;Prior to ES6, we implemented Classes by creating a constructor function and adding properties by extending the prototype:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Person(name, age, gender) {  
    this.name   = name;  
    this.age    = age;  
    this.gender = gender;  
}  

Person.prototype.incrementAge = function () {  
    return this.age += 1;  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And created extended classes by the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Personal(name, age, gender, occupation, hobby) {  
    Person.call(this, name, age, gender);  
    this.occupation = occupation;  
    this.hobby = hobby;  
}  

Personal.prototype = Object.create(Person.prototype);  
Personal.prototype.constructor = Personal;  
Personal.prototype.incrementAge = function () {  
    Person.prototype.incrementAge.call(this);  
    this.age += 20;  
    console.log(this.age);  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ES6 classes are a simple sugar over the prototype-based OO pattern. Classes support prototype-based inheritance, super calls, instance and static methods and constructors:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class Person {  
    constructor(name, age, gender) {  
        this.name   = name;  
        this.age    = age;  
        this.gender = gender;  
    }  

    incrementAge() {  
      this.age += 1;  
    }  
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And extend them using the &lt;code class=&quot;language-text&quot;&gt;extends&lt;/code&gt; keyword:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class Personal extends Person {  
    constructor(name, age, gender, occupation, hobby) {  
        super(name, age, gender);  
        this.occupation = occupation;  
        this.hobby = hobby;  
    }  

    incrementAge() {  
        super.incrementAge();  
        this.age += 20;  
        console.log(this.age);  
    }  
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Destructuring]]></title><description><![CDATA[Destructuring is a convenient way of extracting multiple values from data stored in (possibly nested) objects and arrays.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-destructuring/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-destructuring/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_YujTHdJ1Hx9AWIe0CalxPg.png&quot; alt=&quot;ES6 Destructuring&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Destructuring&lt;/h3&gt;
&lt;p&gt;Destructuring is a convenient way of extracting multiple values from data stored in (possibly nested) objects and Arrays.&lt;/p&gt;
&lt;h3&gt;Array&lt;/h3&gt;
&lt;p&gt;Destructuring assignment allows you to assign the properties of an array using syntax that looks similar to array literals.&lt;/p&gt;
&lt;p&gt;Old way:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var first = someArray[0];  
var second = someArray[1];  
var third = someArray[2];&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;New way:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let [first, second, third] = someArray;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you want to declare your variables at the same time, you can add a &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; in front of the assignment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var [ variable1, variable2, ..., variableN ] = array;  
let [ variable1, variable2, ..., variableN ] = array;  
const [ variable1, variable2, ..., variableN ] = array;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can even skip a few variables:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let [,,third] = [&quot;foo&quot;, &quot;bar&quot;, &quot;baz&quot;];  
console.log(third); // &quot;baz&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There’s also no need to match the full array:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let array = [1, 2, 3, 4];  
let [a, b, c] = array;

console.log(a, b, c) // -------- 1  2  3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can capture all trailing items in an array with a “rest” pattern:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const array = [1, 2, 3, 4];  
const [head, ...tail] = array;  
console.log(head); // 1  
console.log(tail); // [2, 3, 4]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Rest parameter must be applied as the last element, otherwise you’ll get a &lt;code class=&quot;language-text&quot;&gt;SyntaxError&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let array = [1, 2, 3, 4];  
let [...head, d] = array;  
// Uncaught SyntaxError: Unexpected token...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Object&lt;/h3&gt;
&lt;p&gt;Old way of destructuring an object:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var person = { first_name: &apos;Joe&apos;, last_name: &apos;Appleseed&apos; };  
var first_name = person.first_name; // &apos;Joe&apos;  
var last_name = person.last_name; // &apos;Appleseed&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;New way of destructuring an object:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let person = { first_name: &apos;Joe&apos;, last_name: &apos;Appleseed&apos; };  
let {first_name, last_name} = person;

console.log(first_name); // &apos;Joe&apos;  
console.log(last_name); // &apos;Appleseed&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you destructure on properties that are not defined, you get undefined:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let { missing } = {};  
console.log(missing); // undefined&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also destructure in a for-of loop:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const arr = [&apos;a&apos;, &apos;b&apos;];  
for (const [index, element] of arr.entries()) {  
    console.log(index, element);  
}  
// Output:  
// 0 a  
// 1 b&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can use rest operator on an object as well (ES7):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let object = {  
  a: &apos;A&apos;,  
  b: &apos;B&apos;,  
  c: &apos;C&apos;,  
  d: &apos;D&apos;,  
}

const { a, b, ...other } = object; // es7  
console.log(other); // {c: &apos;C&apos;, d: &apos;D&apos;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Generators]]></title><description><![CDATA[A generator is a function which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-generators/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-generators/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_rwj0mwY2iJ391EP3EVb0PA.png&quot; alt=&quot;ES6 generators&quot;&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;generator&lt;/code&gt; is a function which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.&lt;/p&gt;
&lt;p&gt;Generators in JavaScript are a very powerful tool for asynchronous programming as they mitigate the problems with callbacks, such as &lt;code class=&quot;language-text&quot;&gt;Callback Hell&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Inversion of Control&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This pattern is what &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt; functions are built on top of.&lt;/p&gt;
&lt;p&gt;For creating a generator function, we use &lt;code class=&quot;language-text&quot;&gt;function *&lt;/code&gt; syntax instead of just &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead. When the iterator’s &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt; method is called, the generator function&apos;s body is executed until the first &lt;code class=&quot;language-text&quot;&gt;yield&lt;/code&gt; expression, which specifies the value to be returned from the iterator or, with &lt;code class=&quot;language-text&quot;&gt;yield*&lt;/code&gt;, delegates to another generator function.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt; method returns an object with a value property containing the yielded value and a done property which indicates whether the generator has yielded its last value as a boolean. Calling the &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt; method with an argument will resume the generator function execution, replacing the yield expression where execution was paused with the argument from &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple example:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function* generator(i) {  
  yield i;  
  yield i + 10;  
}  

var gen = generator(10);  

console.log(gen.next().value);// expected output: 10  
console.log(gen.next().value); // expected output: 20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Example with yield*:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function* anotherGenerator(i) {  
  yield i + 1;  
  yield i + 2;  
  yield i + 3;  
}  

function* generator(i) {  
  yield i;  
  yield* anotherGenerator(i);  
  yield i + 10;  
}  

var gen = generator(10);  

console.log(gen.next().value); // 10  
console.log(gen.next().value); // 11  
console.log(gen.next().value); // 12  
console.log(gen.next().value); // 13  
console.log(gen.next().value); // 20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Infinite Data generator example:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function * naturalNumbers() {  
  let num = 1;  
  while (true) {  
    yield num;  
    num = num + 1  
  }  
}  
const numbers = naturalNumbers();

console.log(numbers.next().value) // 1
console.log(numbers.next().value) // 2 &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Getter and setter functions]]></title><description><![CDATA[Let's take a look at getter and setter functions within ES6 classes. ]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-getter-and-setter-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-getter-and-setter-functions/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_rt1b55HKcfIWZQf-olDhbQ.png&quot; alt=&quot;ES6 getter and setter functions&quot;&gt;&lt;/p&gt;
&lt;p&gt;ES6 has started supporting getter and setter functions within classes. Using the following example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class Person {  
    constructor(name) {  
        this._name = name;  
    }  

    get name() {  
      if(this._name) {  
        return this._name.toUpperCase();    
      } else {  
        return undefined;  
      }    
    }  

    set name(newName) {  
      if (newName == this._name) {  
        console.log(&apos;I already have this name.&apos;);  
      } else if (newName) {  
        this._name = newName;  
      } else {  
        return false;  
      }  
    }  
}  

let person = new Person(&quot;John Doe&quot;);  

// uses the get method in the background  
if (person.name) {  
  console.log(person.name);  // John Doe  
}  

// uses the setter in the background  
person.name = &quot;Jane Doe&quot;;  
console.log(person.name);  // Jane Doe&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Helpful array functions]]></title><description><![CDATA[Dive into new array functions introduced in ES6]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-array-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-array-functions/</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ES6]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1__1bFnrLKJfM9oDP_twk8Pg.png&quot; alt=&quot;ES6 array functions&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**from**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const inventory = [  
    {name: &apos;mars&apos;, quantity: 2},  
    {name: &apos;snickers&apos;, quantity: 3}  
];  
console.log(Array.from(inventory, item =&gt; item.quantity + 2)); // [4, 5]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**of**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Array.of(&quot;Twinkle&quot;, &quot;Little&quot;, &quot;Star&quot;); // returns [&quot;Twinkle&quot;, &quot;Little&quot;, &quot;Star&quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**find**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const inventory = [  
    {name: &apos;mars&apos;, quantity: 2},  
    {name: &apos;snickers&apos;, quantity: 3}  
];  
console.log(inventory.find(item =&gt; item.name === &apos;mars&apos;)); // {name: &apos;mars&apos;, quantity: 2}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**findIndex**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const inventory = [  
    {name: &apos;mars&apos;, quantity: 2},  
    {name: &apos;snickers&apos;, quantity: 3}  
];  
console.log(inventory.findIndex(item =&gt; item.name === &apos;mars&apos;)); // 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**fill**&lt;/code&gt; method takes up to three arguments value, start and end. The start and end arguments are optional with default values of 0 and the length of the this object.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3].fill(1); // [1, 1, 1]  
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Helpful string functions]]></title><description><![CDATA[Quick look at some helpful string functions introduced in ES6]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-string-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-string-functions/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_1FqOzjfkPr33etNp8oqYew.png&quot; alt=&quot;ES6 string functions&quot;&gt;&lt;/p&gt;
&lt;h3&gt;.includes( )&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var string = &apos;string&apos;;  
var substring = &apos;str&apos;;  

console.log(string.indexOf(substring) &gt; -1);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead of checking for a return value &lt;code class=&quot;language-text&quot;&gt;&gt; -1&lt;/code&gt; to denote string containment, we can simply use &lt;code class=&quot;language-text&quot;&gt;.includes()&lt;/code&gt; which will return a boolean:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const string = &apos;string&apos;;  
const substring = &apos;str&apos;;  

console.log(string.includes(substring)); // true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;.repeat( )&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function repeat(string, count) {  
    var strings = [];  
    while(strings.length &amp;lt; count) {  
        strings.push(string);  
    }  
    return strings.join(&apos;&apos;);  
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In ES6, we now have access to a nicer implementation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// String.repeat(numberOfRepetitions)  
&apos;str&apos;.repeat(3); // &apos;strstrstr&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Map & WeakMap]]></title><description><![CDATA[Quick look into new Map & WeakMap introduced in ES6]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-map-weakmap/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-map-weakmap/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_nK_beIXw-lIf1wTpd2kocg.png&quot; alt=&quot;ES6 Map snippet&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Map&lt;/h3&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; is a data structure allows to associate data to a key.&lt;/p&gt;
&lt;p&gt;Before it’s intruduction in ES6, people generally used objects as maps, by associating some object or value to a specific key value:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const person = {}  
person.name = &apos;John&apos;  
person.age = 18  

console.log(person.name) //John  
console.log(person.age) //18&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const person = new Map()  

person.set(&apos;name&apos;, &apos;John&apos;)  
person.set(&apos;age&apos;, 18)  

const name = person.get(&apos;name&apos;)  
const age = person.get(&apos;age&apos;)  

console.log(name) //John  
console.log(age) //18&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; also provide us with methods to help us manage the data.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;delete()&lt;/code&gt; method - deletes an item from a map by key:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;person.delete(&apos;name&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;clear()&lt;/code&gt; method - delete all items from a map:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;person.clear()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;has()&lt;/code&gt; method - check if a map contains an item by key:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const hasName = person.has(&apos;name&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;size()&lt;/code&gt; method - check the number of items in a map:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const size = person.size&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can also use a couple of methods to iterate:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;entries()&lt;/code&gt; — get all entries&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;keys()&lt;/code&gt; — get only all keys&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;values()&lt;/code&gt; — get only all values&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;WeakMap&lt;/h3&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; is a special kind of map.&lt;/p&gt;
&lt;p&gt;In a &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;, items are never garbage collected. A &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; instead lets all its items be freely garbage collected. Every key of a &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; is an object. When the reference to this object is lost, the value can be garbage collected.&lt;/p&gt;
&lt;p&gt;Main differences between &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you cannot iterate over the keys or values (or key-values) of a WeakMap&lt;/li&gt;
&lt;li&gt;you cannot clear all items from a WeakMap&lt;/li&gt;
&lt;li&gt;you cannot check its size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A WeakMap exposes those methods, which are equivalent to the Map ones:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;get(k)  
set(k, v)  
has(k)  
delete(k)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The use cases of a &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; are less evident than the ones of a &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;, and you might never find the need for them, but essentially it can be used to build a memory-sensitive cache that is not going to interfere with garbage collection, or for careful encapsualtion and information hiding.&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Modules]]></title><description><![CDATA[Prior to ES6, we used libraries such as Browserify to create modules on the client-side, and require in Node.js. With ES6, we can now directly use modules of all types (AMD and CommonJS).]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-modules/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-modules/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_AVuC2PTg3VOE6EEZTfnydw.png&quot; alt=&quot;ES6 modules&quot;&gt;&lt;/p&gt;
&lt;p&gt;Prior to ES6, we used libraries such as &lt;a href=&quot;http://browserify.org/&quot;&gt;Browserify&lt;/a&gt; to create modules on the client-side, and &lt;a href=&quot;https://nodejs.org/api/modules.html#modules_module_require_id&quot;&gt;require&lt;/a&gt; in &lt;strong&gt;Node.js&lt;/strong&gt;. With ES6, we can now directly use modules of all types (AMD and CommonJS).&lt;/p&gt;
&lt;h3&gt;Exporting in CommonJS&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;module.exports = 1;  
module.exports = { foo: &apos;bar&apos; };  
module.exports = [&apos;foo&apos;, &apos;bar&apos;];  
module.exports = function bar () {};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Exporting in ES6&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Named Exports:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;export function multiply (x, y) {  
  return x * y;  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As well as &lt;strong&gt;exporting a list&lt;/strong&gt; of objects:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function add (x, y) {  
  return x + y;  
};  

function multiply (x, y) {  
  return x * y;  
};  

export { add, multiply };&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Default export:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In our module, we can have many named exports, but we can also have a default export. It’s because our module could be a large library and with default export we can import then an entire module.&lt;/p&gt;
&lt;p&gt;Important to note that there’s only &lt;code class=&quot;language-text&quot;&gt;one default export per module&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;export default function (x, y) {  
  return x * y;  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This time we don’t have to use curly braces for importing and we have a chance to name imported statement as we wish.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import multiply from &apos;module&apos;;  
// === OR ===  
import whatever from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A module can have both named exports and a default export:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// module.js  
export function add (x, y) {  
  return x + y;  
};  
export default function (x, y) {  
  return x * y;  
};  

// app.js  
import multiply, { add } from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The default export is just a named export with the special name default.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// module.js  
export default function (x, y) {  
  return x * y;  
};  

// app.js  
import { default } from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Importing in ES6&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import { add } from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can even import many statements:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import { add, multiply } from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Imports may also be &lt;strong&gt;aliased&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import {   
  add as addition,   
  multiply as multiplication  
} from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and use wildcard (&lt;code class=&quot;language-text&quot;&gt;*&lt;/code&gt;) to import all exported statemets:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import * from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Promises]]></title><description><![CDATA[Promises are one of the most exciting additions to JavaScript ES6. Promises are a pattern that greatly simplifies asynchronous programming by making the code look synchronous and avoid problems associated with callbacks.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-promises/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-promises/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_AqkCUN-kD_fLefEFPnX2Uw.png&quot; alt=&quot;ES6 Promises&quot;&gt;&lt;/p&gt;
&lt;p&gt;Promises are one of the most exciting additions to JavaScript ES6. Promises are a pattern that greatly simplifies asynchronous programming by making the code look synchronous and avoid problems associated with callbacks.&lt;/p&gt;
&lt;p&gt;Prior to ES6, we used &lt;a href=&quot;https://github.com/petkaantonov/bluebird&quot;&gt;bluebird&lt;/a&gt; or &lt;a href=&quot;https://github.com/kriskowal/q&quot;&gt;Q&lt;/a&gt;. Now we have Promises natively.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;resolve&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;reject&lt;/code&gt; are functions themselves and are used to send back values to the promise object.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const myPromise = new Promise((resolve, reject) =&gt; {  
    if (Math.random() * 100 &amp;lt;= 90) {  
        resolve(&apos;Hello, Promises!&apos;);  
    }  
    reject(new Error(&apos;In 10% of the cases, I fail. Miserably.&apos;));  
});  

myPromise.then((resolvedValue) =&gt; {  
    console.log(resolvedValue); //Hello, Promises!  
}, (error) =&gt; {  
    console.log(error); //In 10% of the cases, I fail. Miserably.  
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Chaining Promises:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Promises allow us to turn our horizontal code (callback hell):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;func1(function (value1) {  
    func2(value1, function (value2) {  
        func3(value2, function (value3) {  
          // Do something with value 3  
        });  
    });  
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Into vertical code like so:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;func1(value1)  
    .then(func2)  
    .then(func3, value3 =&gt; {  
        // Do something with value 3  
    });&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Parallelize Promises:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We can use &lt;code class=&quot;language-text&quot;&gt;Promise.all()&lt;/code&gt; to handle an array of asynchronous operations.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let urls = [  
  &apos;/api/commits&apos;,  
  &apos;/api/issues/opened&apos;,  
  &apos;/api/issues/assigned&apos;,  
  &apos;/api/issues/completed&apos;,  
  &apos;/api/issues/comments&apos;,  
  &apos;/api/pullrequests&apos;  
];  

let promises = urls.map((url) =&gt; {  
  return new Promise((resolve, reject) =&gt; {  
    $.ajax({ url: url })  
      .done((data) =&gt; {  
        resolve(data);  
      });  
  });  
});  

Promise.all(promises)  
  .then((results) =&gt; {  
    // Do something with results of all our promises  
 });&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Set & WeakSet]]></title><description><![CDATA[A Set is a collection for unique values. The values can be primitives or object references.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-set-weakset/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-set-weakset/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_AJDn2sPnvaDVOHZo2F3Zaw.png&quot; alt=&quot;ES6 Set&quot;&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; is a collection for unique values. The values can be primitives or object references.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let set = new Set();  
set.add(1);  
set.add(&apos;1&apos;);  
set.add({ key: &apos;value&apos; });  
console.log(set); // Set {1, &apos;1&apos;, Object {key: &apos;value&apos;}}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most importantly is that it does not allow duplicate values, one good use if to remove duplicate values from an array:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[ ...new Set([1, 2, 3, 1, 2, 3]) ] //[1, 2, 3]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Iteration using built-in method forEach and for..of:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// forEach  
let set = new Set([1, &apos;1&apos;, { key: &apos;value&apos; }]);  
set.forEach(function (value) {  
  console.log(value);  
  // 1  
  // &apos;1&apos;  
  // Object {key: &apos;value&apos;}  
});

// for..of  
let set = new Set([1, &apos;1&apos;, { key: &apos;value&apos; }]);  
for (let value of set) {  
  console.log(value);  
  // 1  
  // &apos;1&apos;  
  // Object {key: &apos;value&apos;}  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Similar to &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; provides us with methods such as &lt;code class=&quot;language-text&quot;&gt;has()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;delete()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;clear()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;WeakSet&lt;/h3&gt;
&lt;p&gt;Like a &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;WeakSet&lt;/code&gt; is a &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; that doesn’t prevent its values from being garbage-collected. It has simpler API than &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt;, because has only three methods:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;new WeakSet([iterable])  
WeakSet.prototype.add(value)    : any  
WeakSet.prototype.has(value)    : boolean  
WeakSet.prototype.delete(value) : boolean&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Important thing to note &lt;code class=&quot;language-text&quot;&gt;WeakSet&lt;/code&gt; is a collection that can‘t be iterated and whose size cannot be determined.&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;WeakSet&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet  —  Spread Operator]]></title><description><![CDATA[The spread syntax is simply three dots: `...` It allows an iterable to expand in places where 0+ arguments are expected.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-spread-operator/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-spread-operator/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_rA3qD8SBiE_BrmeWuufvYw.png&quot; alt=&quot;ES6 Spread Operator&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Spread Operator&lt;/h3&gt;
&lt;p&gt;The spread syntax is simply three dots: &lt;code class=&quot;language-text&quot;&gt;...&lt;/code&gt; It allows an iterable to expand in places where 0+ arguments are expected.&lt;/p&gt;
&lt;h3&gt;Calling Functions without Apply:&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function doStuff (x, y, z) { }  
var args = [0, 1, 2];  

// Call the function, passing args  
doStuff.apply(null, args);

doStuff(...args);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using spread operator:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const arr = [2, 4, 8, 6, 0];  
const max = Math.max(...arr);  

console.log(max); //8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or another example using Math functions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let mid = [3, 4];  
let arr = [1, 2, ...mid, 5, 6]; //[1, 2, 3, 4, 5, 6]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Combine arrays&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let arr = [1,2,3];  
let arr2 = [...arr]; // like arr.slice()  
arr2.push(4)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet — String Templates]]></title><description><![CDATA[Template Strings use back-ticks (``) rather than the single or double quotes we’re used to with regular strings.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-string-templates/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-string-templates/</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ES6]]></category><pubDate>Tue, 16 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_OxzGYSWzbivvvMcTkbjC9w.png&quot; alt=&quot;string_templates&quot;&gt;&lt;/p&gt;
&lt;p&gt;Template Strings use back-ticks (``) rather than the single or double quotes we’re used to with regular strings. A template string could thus be written as follows:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const greeting = `Yo World!`;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;String Substitution&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;Substitution allows us to place any valid JavaScript expression inside a Template Literal, the result will be output as part of the same string.&lt;/p&gt;
&lt;p&gt;Template Strings can contain placeholders for string substitution using the ${ } syntax:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var name = &quot;Brendan&quot;;
console.log(`Yo, ${name}!`); //&quot;Yo, Brendan!&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can use expression interpolation to embed for some readable inline math:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var a = 10;
var b = 10;
console.log(`${a+b}`); //20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;They are also very useful for functions inside expressions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function fn() { return &quot;inside fn&quot;; }
console.log(`outside, ${fn()}, outside`); // outside, inside fn, outside.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Multiline Strings:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Multiline strings in JavaScript have required hacky workarounds for some time. Template Strings significantly simplify multiline strings. Simply include newlines where they are needed and BOOM.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let text = `In ES5 this is
not legal.`&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Unescaped template strings:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We can now construct strings that have special characters in them without needing to escape them explicitly.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var text = &quot;This string contains &quot;double quotes&quot; which are escaped.&quot;;
let text = `This string contains &quot;double quotes&quot; which don&apos;t need to be escaped anymore.`;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet — Variable Declarations]]></title><description><![CDATA[An introduction to ES6 variable declarations, differences between var, let, const.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-variable-declarations/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-variable-declarations/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Tue, 16 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_T6qcNaaF8T0HedbwF2ZmyQ.png&quot; alt=&quot;es6-variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;ES6 brought &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; with proper lexical scoping. &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; is the new &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;. Constants work just like &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, but can’t be reassigned. &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; are block scoped. Therefore, referencing block-scoped identifiers before they are defined will produce a &lt;code class=&quot;language-text&quot;&gt;ReferenceError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Example using &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var variable = 5;

{
  console.log(&apos;inside&apos;, variable); //5
  var variable = 10;
}

console.log(&apos;outside&apos;, variable); //10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example using &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const variable = 5;

variable = variable*2; // TypeError: Attempted to assign to readonly property.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Constants are tricky with array and objects. The &lt;code class=&quot;language-text&quot;&gt;reference&lt;/code&gt; becomes constant but the value does not.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const variable = [5];

console.log(variable) // [5]

variable = [2]; //TypeError: Attempted to assign to readonly property.

variable[0] = 1;
console.log(variable) // [1]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How I grew my Twitter followers from 500 to 1000 in just 12 days 👏👏👏]]></title><description><![CDATA[An experiment on growing my twitter following.]]></description><link>https://mihaiserban.dev/blog/how-i-grew-my-twitter-followers-from-500-to-1000-in-just-12-days/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/how-i-grew-my-twitter-followers-from-500-to-1000-in-just-12-days/</guid><category><![CDATA[Social Media]]></category><category><![CDATA[Growth Hacking]]></category><pubDate>Fri, 11 May 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_T1DMihbZ4Pjc7v6UdxvX7w.png&quot; alt=&quot;twitter_stats&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Twitter can sometimes can make you feel like you’re tweeting to a lonesome abyss.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before we dive into the How To, let me tell you that I’m not a social media marketing expert. I’m a software engineer and #socialmedia doesn’t come natural to me 😅.&lt;/p&gt;
&lt;p&gt;My twitter followers have always stagnated at around 300–400. This is mostly because I was just consuming information and didn’t spend time sharing &lt;strong&gt;relevant tweets&lt;/strong&gt; and &lt;strong&gt;building an audience&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Recently I’ve decided to do an experiment and see how far I can take my Twitter account. So let’s get down to it 🧐&lt;/p&gt;
&lt;p&gt;My Twitter followers before doing this experiment: &lt;strong&gt;435&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After doing some research I made a plan:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setup profile bio, be authenthic, write what your passionate about.&lt;/li&gt;
&lt;li&gt;Add a profile photo.. nobody wants to follow an “egghead” 😒&lt;/li&gt;
&lt;li&gt;Identify my audience. My audience revolves around #swift #objectivec #javascript #graphql #reactjs #nodejs 👍🏻. Use tools such as &lt;a href=&quot;https://hashtagify.me/&quot;&gt;hashtagify.me&lt;/a&gt; to research what hashtags to use.&lt;/li&gt;
&lt;li&gt;Engage audience with relevant tweets. Be consistent! (post 3–6 times a day). Tweeting/retweeting too much will hurt your profile.&lt;/li&gt;
&lt;li&gt;Follow and engage with &lt;strong&gt;active&lt;/strong&gt; people with the same interests as yourself. Search through relevant hasthtags (eg.: #technology) and &lt;strong&gt;engage with the people that actively like and retweet other people’s posts&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Engage in Twitter chats. This can lead to massive exposure.&lt;/li&gt;
&lt;li&gt;PRO TIP: pin your most engaging tweets to your twitter profile. Use &lt;a href=&quot;https://analytics.twitter.com/&quot;&gt;Twitter Analytics&lt;/a&gt; to find your most popular tweets.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to tweet to my twitter audience I’ve setup a workflow using the following tools: &lt;a href=&quot;https://buffer.com/&quot;&gt;Buffer&lt;/a&gt;, &lt;a href=&quot;https://feedly.com/&quot;&gt;Feedly&lt;/a&gt; and &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://feedly.com/&quot;&gt;Feedly&lt;/a&gt; let’s you find and subscribe to quality publications. Also can easily be plugged into services such as &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt; or &lt;a href=&quot;https://ifttt.com/&quot;&gt;IFTTT&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_wTj61rb3GZMKDcURL_aM5w.png&quot; alt=&quot;feedly_screen&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt; allows you to create jobs which check if a new article was published into a Feedly category and then automatically add it to my Buffer social media queue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_4r3hfl0rWW51jG6CnTsSpQ.png&quot; alt=&quot;zapier_screenshot&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://buffer.com/&quot;&gt;Buffer&lt;/a&gt; is the last piece of the puzzle. The service schedules content to be sent out to my social media profile. TIP: Make sure to keep that queue filled and review it once every couple of days. &lt;strong&gt;Add hashtags, mention authors for extra engagment&lt;/strong&gt; 👌&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_aZjiPD1cuhn92DtDVU8Q3g.png&quot; alt=&quot;buffer_screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;One takaway is that consitency is key… Building an audience takes time. Treat your Twitter profile like you were running a marathon, not a sprint.
Let me know if you have any other tips for growing your social media presence 🙏🏻&lt;/p&gt;
&lt;p&gt;See you on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;
Happy &lt;em&gt;#GrowthHacking&lt;/em&gt;!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to generate cryptocurrency time intervals using MongoDB Aggregation Framework and Node.js]]></title><description><![CDATA[Let's use MongoDB Aggregation Framework to track volume changes at 1/5/30 minutes intervals.]]></description><link>https://mihaiserban.dev/blog/aggregate-mongodb-data-with-node-js-and-mongoose-cryptocurrency-financial-time-series/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/aggregate-mongodb-data-with-node-js-and-mongoose-cryptocurrency-financial-time-series/</guid><category><![CDATA[NodeJS]]></category><category><![CDATA[MongoDB]]></category><pubDate>Wed, 23 Aug 2017 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/0_m4PdT9e8rKaVnWvD.jpeg&quot; alt=&quot;bitcoin&quot;&gt;&lt;/p&gt;
&lt;p&gt;Cryptocurrency is known for having major price swings, big players often do what’s called a pump and dump. They come into the markets with billions of dollars, buy a lot of tokens and steadily increase the price, create FOMO (fear of missing out), buying pressure, and then dump the tokens at a higher price making a big profits.&lt;/p&gt;
&lt;p&gt;If we wanted to analyze this kind of market movements we’ll need to gather data from somewhere.&lt;/p&gt;
&lt;p&gt;Fortunately exchanges provide REST and socket APIs for their market data, but mostly include 24h changes, and no method to retrieve smaller intervals such as 1 minute data, 5 minute, 30 minute or more.
We’ll use &lt;a href=&quot;https://docs.bitfinex.com/v1/reference&quot;&gt;Bitfinex&lt;/a&gt; ticker data for this example.&lt;/p&gt;
&lt;p&gt;What is ticker data? A tick is a measure of the minimum upward or downward movement in the price of a security or it can also refer to the change in the price of a security from trade to trade.
Example of a tick provided by Bitfinex:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{
  &quot;mid&quot;:&quot;244.755&quot;, (bid + ask) / 2
  &quot;bid&quot;:&quot;244.75&quot;, Innermost bid
  &quot;ask&quot;:&quot;244.76&quot;, Innermost ask
  &quot;last_price&quot;:&quot;244.82&quot;, The price at which the last order executed
  &quot;low&quot;:&quot;244.2&quot;, Lowest trade price of the last 24 hours
  &quot;high&quot;:&quot;248.19&quot;, Highest trade price of the last 24 hours
  &quot;volume&quot;:&quot;7842.11542563&quot;, Trading volume of the last 24 hours
  &quot;timestamp&quot;:&quot;1444253422.348340958&quot; The timestamp at which this information was valid
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see the data provided by Bitfinex represents mostly 24h changes, highs and lows for the day, volume etc. We’re interested in the timestamp and last_price, which reflect the price of the asset at the current time.&lt;/p&gt;
&lt;p&gt;We’ll take all this data and store it in a MongoDB database.&lt;/p&gt;
&lt;p&gt;Our model looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var tickerSchema = new Schema({
  bid: Number,
  bid_size: Number,
  ask: Number,
  ask_size: Number,
  daily_change: Number,
  daily_change_perc: Number,
  last_price: Number,
  volume: Number,
  high: Number,
  low: Number,
  created_at: { type: Date, required: true, default: Date.now },
  symbol: String,
  exchange: String
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that we have the data store we can go ahead and create our &lt;a href=&quot;https://docs.mongodb.com/manual/aggregation/&quot;&gt;Mongo Aggregation pipeline&lt;/a&gt; to extract the ticks in time intervals we need.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const pair = &apos;tETHUSD&apos;
const exchange = &apos;Bitfinex&apos;
const periods = 5; //time intervals to process data
const minutesAgo = 30;
let startDate = new Date()
startDate.setMinutes(timeAgo.getMinutes()-minutesAgo)
const endDate = new Date()
const operations = [
      {
        $match: {
          created_at: {$gte: startDate, $lt: endDate},
          symbol: pair,
          exchange: exchange
        }
      },
      {
        $group: {
          _id: {
            $add: [
                { $subtract: [
                    { $subtract: [ &quot;$created_at&quot;, new Date(0) ] },
                    { $mod: [
                        { $subtract: [ &quot;$created_at&quot;, new Date(0) ] },
                        1000 * 60 * period
                    ]}
                ]}, new Date(0)]
          },
          first_close: {$first: &quot;$last_price&quot;},
          last_close: {$last: &quot;$last_price&quot;},
          first_volume: {$first: &quot;$volume&quot;},
          last_volume: {$last: &quot;$volume&quot;},
          high: {$max: &quot;$last_price&quot;},
          low: {$min: &quot;$last_price&quot;},
        }
      },
      {
        $project: {
          _id: 1,
          period_change: { $subtract: [ &apos;$last_close&apos;, &apos;$first_close&apos; ] },
          period_change_perc: (1 - (&apos;$first_close&apos; / &apos;$last_close&apos;)),
          open: &apos;$first_close&apos;,
          close: &apos;$last_close&apos;,
          volume: { $subtract: [ &apos;$last_volume&apos;, &apos;$first_volume&apos; ] },
          high: &apos;$high&apos;,
          low: &apos;$low&apos;
        }
      },
      {
        $sort: {
          _id: 1
        }
      }
    ];
Ticker.aggregate(operations,function(err, results) {
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our aggregation pipeline consists of 4 operations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;$match&lt;/strong&gt; : filters the tick data based on date, symbol and exchange&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$group&lt;/strong&gt; : groups the data returned by the match operation into periods, in our case 5 minutes. Here we also compute additional value such as the new highs and lows for the perios, first close, last close, first volume, last volume. All this data can be extracted from the last close price and volume of the ticker.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$project&lt;/strong&gt; : the data resulted from the $group operation is passed into the $project phase, where we use &lt;a href=&quot;https://docs.mongodb.com/manual/reference/operator/aggregation/&quot;&gt;operators&lt;/a&gt; to compute the final output. For example period_change is &lt;em&gt;last_close&lt;/em&gt;-&lt;em&gt;first_close&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$sort&lt;/strong&gt; : Make sure we sort the periods in ascending order&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Success!&lt;/p&gt;
&lt;p&gt;Output of our aggregation is an array of aggregated tickers at 5 minute intervals:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[ 
  { _id: 2017-08-24T07:15:00.000Z,
    period_change: 0.2400000000000091,
    open: 319.2,
    close: 319.44,
    volume: -52.8511199200002,
    high: 319.44,
    low: 319.2 },
  { _id: 2017-08-24T07:20:00.000Z,
    period_change: 0.07999999999998408,
    open: 319.44,
    close: 319.52,
    volume: 18.845093469994026,
    high: 319.52,
    low: 319.44 } 
 ]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;P.S. If you ❤️ this, make sure to follow me on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;, and share this with your friends 😀🙏🏻&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aggregation - MongoDB Manual 3.4&lt;/strong&gt;
&lt;a href=&quot;https://docs.mongodb.com/manual/aggregation/&quot;&gt;Aggregation operations process data records and return computed results.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;General&lt;/strong&gt;
&lt;a href=&quot;https://docs.bitfinex.com/v2/docs/ws-general&quot;&gt;Current Version Bitfinex Websocket API version is 2.0&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>