<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Alessandro Bahgat&apos;s Blog</title><description>Personal website of Alessandro Bahgat — Software Engineering Leader.</description><link>https://www.abahgat.com</link><item><title>Visualizing Ukkonen&apos;s Suffix Tree Algorithm</title><link>https://www.abahgat.com/blog/visualizing-ukkonens-algorithm</link><guid isPermaLink="true">https://www.abahgat.com/blog/visualizing-ukkonens-algorithm</guid><description>I learned algorithms from textbooks and papers, building mental models from pseudocode and hand-drawn sketches. The hardest part was never reading the algorithm: it was seeing what it was actually doing to the data structure in memory. This post is the tool I wish I had back then.</description><pubDate>Mon, 09 Mar 2026 15:31:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;learning-algorithms-from-books&quot;&gt;Learning algorithms from books&lt;/h2&gt;
&lt;p&gt;I learned most of what I know about algorithms by poring over a copy of &lt;em&gt;Introduction to Algorithms&lt;/em&gt; I got while in university. The book is very well known, especially among folks who got a formal education in computer science.&lt;/p&gt;
&lt;figure class=&quot;my-6&quot;&gt; &lt;img src=&quot;https://www.abahgat.com/_astro/clrs-book.5_3f5iDF.jpg&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot; width=&quot;4080&quot; height=&quot;2141&quot; srcset=&quot;https://www.abahgat.com/_astro/clrs-book.5_3f5iDF_Z1ASvQV.webp 400w, /_astro/clrs-book.5_3f5iDF_WJsQr.webp 768w, /_astro/clrs-book.5_3f5iDF_Z1bgESu.webp 1024w, /_astro/clrs-book.5_3f5iDF_Z280kAG.webp 2040w, /_astro/clrs-book.5_3f5iDF_na0IR.webp 4080w, /_astro/clrs-book.5_3f5iDF_1qJgVU.webp 8160w&quot; sizes=&quot;(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px&quot; style=&quot;object-fit: cover; object-position: center; max-width: 4080px; max-height: 2141px; aspect-ratio: 1.9056515646893974; width: 100%;&quot; alt=&quot;If you have studied it, you know the book: it is over a thousand pages long and it weighs enough to double as a doorstop.&quot; class=&quot;mx-auto rounded-md shadow-lg bg-gray-400 dark:bg-slate-700 w-full&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt; &lt;figcaption class=&quot;mt-2 text-center text-sm text-gray-500 dark:text-muted&quot;&gt; If you have studied it, you know the book: it is over a thousand pages long and it weighs enough to double as a doorstop. &lt;/figcaption&gt; &lt;/figure&gt;
&lt;p&gt;I worked through large sections of it, pen in hand, trying to trace through increasingly complex algorithms, building intuition for their behavior and tradeoffs. The book covers the theory in great depth: correctness proofs, recurrence relations, asymptotic analysis.&lt;/p&gt;
&lt;p&gt;But there was often a gap between reading an algorithm and truly understanding it. The book would present pseudocode, sometimes a few diagrams showing state at key moments and theorems about performance characteristics. The work of tracing what actually happens was left as an exercise to the reader. I did that work with pen and paper, drawing trees, crossing out nodes, scribbling indices in the margins. It worked, eventually. But it was slow, error-prone, and the understanding felt fragile.&lt;/p&gt;
&lt;h2 id=&quot;implementing-from-a-paper&quot;&gt;Implementing from a paper&lt;/h2&gt;
&lt;p&gt;Years later, I ran into this gap again. I was working on a &lt;a href=&quot;https://www.abahgat.com/blog/the-programming-puzzle-that-got-me-my-job/&quot;&gt;programming puzzle&lt;/a&gt; that required near-instant substring search over a large dataset. After some research, I settled on a &lt;a href=&quot;https://www.abahgat.com/project/suffix-tree/&quot;&gt;Generalized Suffix Tree&lt;/a&gt;: a data structure that indexes all suffixes of a set of strings, enabling &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;O(m)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; lookups where &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is the length of the search pattern, even over an extremely large corpus.&lt;/p&gt;
&lt;p&gt;The algorithm I chose for building the tree was Ukkonen’s, described in a &lt;a href=&quot;https://www.cs.helsinki.fi/u/ukkonen/SuffixT1withFigs.pdf&quot;&gt;1995 paper&lt;/a&gt;. The paper is well written and includes the full algorithm in pseudocode:&lt;/p&gt;
&lt;figure class=&quot;my-6&quot;&gt; &lt;img src=&quot;https://www.abahgat.com/_astro/ukkonen-pseudocode.C7lPd-N0.png&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot; width=&quot;890&quot; height=&quot;514&quot; srcset=&quot;https://www.abahgat.com/_astro/ukkonen-pseudocode.C7lPd-N0_Z1pgFT8.webp 400w, /_astro/ukkonen-pseudocode.C7lPd-N0_19w43J.webp 768w, /_astro/ukkonen-pseudocode.C7lPd-N0_ZbEqbH.webp 890w, /_astro/ukkonen-pseudocode.C7lPd-N0_Z7zVcd.webp 1024w, /_astro/ukkonen-pseudocode.C7lPd-N0_Z1LBokq.webp 1780w&quot; sizes=&quot;(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px&quot; style=&quot;object-fit: cover; object-position: center; max-width: 890px; max-height: 514px; aspect-ratio: 1.7315175097276265; width: 100%;&quot; alt=&quot;One of several pseudocode snippets from Ukkonen&apos;s paper, describing the update function. Clear on paper, but its translation to working code is much more verbose than this.&quot; class=&quot;mx-auto rounded-md shadow-lg bg-gray-400 dark:bg-slate-700 w-full&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt; &lt;figcaption class=&quot;mt-2 text-center text-sm text-gray-500 dark:text-muted&quot;&gt; One of several pseudocode snippets from Ukkonen&apos;s paper, describing the update function. Clear on paper, but its translation to working code is much more verbose than this. &lt;/figcaption&gt; &lt;/figure&gt;
&lt;p&gt;It took me a few hours to get right. Not because the pseudocode was wrong: it was precise and correct. The difficulty was that the algorithm manipulates a tree in non-obvious ways. There is an “active point” that walks around the tree. Suffix links connect internal nodes as shortcuts. Three different extension rules fire depending on what is already in the tree and what is being added. The pseudocode tells you &lt;em&gt;what&lt;/em&gt; to do, but building an intuition for &lt;em&gt;why&lt;/em&gt; it works requires watching it happen.&lt;/p&gt;
&lt;p&gt;I did what I always did: I sketched trees by hand. I traced the algorithm on the string &lt;code&gt;cacao&lt;/code&gt;, then on &lt;code&gt;banana&lt;/code&gt;, drawing and redrawing nodes and edges as each character was processed. When my &lt;a href=&quot;https://github.com/abahgat/suffixtree&quot;&gt;Java implementation&lt;/a&gt; finally produced correct results, I was relieved, but my understanding of the algorithm still felt like it had been assembled from fragments.&lt;/p&gt;
&lt;p&gt;The biggest frustration was that I had no way to inspect what my code was actually building. I relied on the usual bag of tricks: print statements, breakpoints, inspecting memory structures one by one in a debugger. But that is like understanding a forest by looking at one tree at a time. What I wanted was to &lt;em&gt;see&lt;/em&gt; the whole data structure after each operation — to watch the algorithm work.&lt;/p&gt;
&lt;h2 id=&quot;the-visualization-i-wish-i-had&quot;&gt;The visualization I wish I had&lt;/h2&gt;
&lt;p&gt;That idea stuck with me: build the algorithm in a language where rendering the data structure is easy, then step through the construction visually. JavaScript and &lt;a href=&quot;https://d3js.org/&quot;&gt;D3.js&lt;/a&gt; are a natural fit: the algorithm produces a tree, and D3 is very good at drawing trees.&lt;/p&gt;
&lt;p&gt;So here it is. The visualization below builds a suffix tree for the string &lt;code&gt;banana&lt;/code&gt; using Ukkonen’s algorithm, step by step. Use the playback controls to move through the construction. The gold-highlighted node is the active point. Dashed arcs are suffix links.&lt;/p&gt;
&lt;div class=&quot;stv-root not-prose my-8&quot; data-stv-config=&quot;{&amp;#34;initialStrings&amp;#34;:[&amp;#34;banana&amp;#34;],&amp;#34;initialSearch&amp;#34;:&amp;#34;&amp;#34;,&amp;#34;height&amp;#34;:500,&amp;#34;showBuilt&amp;#34;:false}&quot;&gt; &lt;!-- Playback + indexed strings --&gt; &lt;div class=&quot;stv-controls flex flex-wrap items-center gap-2 mb-2 p-3 rounded-lg border border-border-default bg-card&quot;&gt; &lt;div class=&quot;flex items-center gap-0.5 text-default&quot;&gt; &lt;button class=&quot;stv-reset p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Reset&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;19 20 9 12 19 4 19 20&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;5&quot; y1=&quot;19&quot; x2=&quot;5&quot; y2=&quot;5&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-prev p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Previous step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;11 19 2 12 11 5 11 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;22 19 13 12 22 5 22 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-play p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Play / Pause&quot;&gt; &lt;svg class=&quot;stv-play-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;svg class=&quot;stv-pause-icon hidden&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;rect x=&quot;6&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;rect x=&quot;14&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-next p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Next step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;13 19 22 12 13 5 13 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;2 19 11 12 2 5 2 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-end p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Skip to end&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;5 4 15 12 5 20 5 4&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;19&quot; y1=&quot;5&quot; x2=&quot;19&quot; y2=&quot;19&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;select class=&quot;stv-speed ml-1.5 px-1.5 py-1 text-xs border border-border-default rounded
                      bg-input text-default cursor-pointer&quot;&gt; &lt;option value=&quot;0.5&quot;&gt;0.5x&lt;/option&gt; &lt;option value=&quot;1&quot; selected&gt;1x&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;2x&lt;/option&gt; &lt;option value=&quot;4&quot;&gt;4x&lt;/option&gt; &lt;/select&gt; &lt;/div&gt; &lt;!-- Indexed strings tags (inline with playback) --&gt; &lt;div class=&quot;stv-strings flex flex-wrap items-center gap-1.5&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;!-- Add / Search --&gt; &lt;div class=&quot;flex flex-wrap items-center gap-2 mb-3&quot;&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-input flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Type a string...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-add px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Add
&lt;/button&gt; &lt;/div&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-search flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Search...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-search-btn px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Search
&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- SVG container + overlay prompt --&gt; &lt;div class=&quot;relative&quot; style=&quot;height: 500px&quot;&gt; &lt;div class=&quot;stv-canvas absolute inset-0 rounded-lg border border-border-default bg-card overflow-hidden&quot;&gt;&lt;/div&gt; &lt;!-- Empty state prompt (sits above the SVG) --&gt; &lt;div class=&quot;stv-prompt absolute inset-0 flex flex-col items-center justify-center text-muted pointer-events-none&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; class=&quot;opacity-40 mb-3&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;p class=&quot;stv-prompt-text text-sm&quot;&gt;Press play to watch the tree being built&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Step description --&gt; &lt;div class=&quot;stv-info mt-3 p-3 rounded-lg border border-border-default bg-card text-sm min-h-[3.5em]&quot;&gt; &lt;p class=&quot;stv-desc text-muted italic&quot;&gt;Add a string to begin building the suffix tree.&lt;/p&gt; &lt;div class=&quot;stv-progress mt-2 hidden&quot;&gt; &lt;div class=&quot;flex justify-between text-xs text-muted mb-1&quot;&gt; &lt;span class=&quot;stv-counter&quot;&gt;Step 0 / 0&lt;/span&gt; &lt;span class=&quot;stv-phase&quot;&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class=&quot;w-full bg-border-default rounded-full h-1&quot;&gt; &lt;div class=&quot;stv-bar bg-primary rounded-full h-1 transition-all duration-200&quot; style=&quot;width: 0%&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Length warning --&gt; &lt;div class=&quot;stv-warning hidden mt-2 p-2 rounded text-xs text-primary border border-primary/30 bg-primary/10&quot;&gt;
Long strings produce large trees that may be hard to read. Use zoom (scroll) and pan (drag) to navigate.
&lt;/div&gt; &lt;/div&gt; 
&lt;p&gt;The paper describes the core logic across Sections 2–4. Here is &lt;code&gt;test_and_split&lt;/code&gt;, the procedure that decides whether the tree needs to grow, which is a companion to the &lt;code&gt;update&lt;/code&gt; function we showed earlier:&lt;/p&gt;
&lt;figure class=&quot;my-6&quot;&gt; &lt;img src=&quot;https://www.abahgat.com/_astro/test-and-split.DH1eV5PB.png&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot; width=&quot;1083&quot; height=&quot;634&quot; srcset=&quot;https://www.abahgat.com/_astro/test-and-split.DH1eV5PB_dTr2u.webp 400w, /_astro/test-and-split.DH1eV5PB_2nzKAo.webp 768w, /_astro/test-and-split.DH1eV5PB_Z2aSOHj.webp 1024w, /_astro/test-and-split.DH1eV5PB_shTPK.webp 1083w, /_astro/test-and-split.DH1eV5PB_1gu88C.webp 2040w, /_astro/test-and-split.DH1eV5PB_kBv51.webp 2166w&quot; sizes=&quot;(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px&quot; style=&quot;object-fit: cover; object-position: center; max-width: 1083px; max-height: 634px; aspect-ratio: 1.7082018927444795; width: 100%;&quot; alt=&quot;Procedure test_and_split from Ukkonen&apos;s paper. It returns true when the next character is already in the tree (the end point), and false after splitting an edge to make room for a new branch.&quot; class=&quot;mx-auto rounded-md shadow-lg bg-gray-400 dark:bg-slate-700 w-full&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt; &lt;figcaption class=&quot;mt-2 text-center text-sm text-gray-500 dark:text-muted&quot;&gt; Procedure test_and_split from Ukkonen&apos;s paper. It returns true when the next character is already in the tree (the end point), and false after splitting an edge to make room for a new branch. &lt;/figcaption&gt; &lt;/figure&gt;
&lt;p&gt;A few things to watch for in the visualization — each one corresponds to something in this procedure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Branching in &lt;code&gt;update&lt;/code&gt;:&lt;/strong&gt; when &lt;code&gt;test_and_split&lt;/code&gt; finds no existing transition for the next character, it splits the edge if needed and &lt;code&gt;update&lt;/code&gt; creates a new leaf. These are the moments where the tree visibly grows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reaching the end point:&lt;/strong&gt; when &lt;code&gt;test_and_split&lt;/code&gt; finds that a transition for the next character already exists, the algorithm has reached what the paper calls the &lt;em&gt;end point&lt;/em&gt; of the current phase. All remaining suffixes are already represented implicitly, so the phase stops. This is the key to the algorithm’s &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;O(n)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; time: the end point can only move forward through the string across phases, bounding the total work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suffix links&lt;/strong&gt; (the paper’s &lt;em&gt;suffix function&lt;/em&gt; &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8889em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em&quot;&gt;f&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;): if an internal node has path-label &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;α&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x\alpha&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0037em&quot;&gt;xα&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, its suffix link points to the node with path-label &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;α&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\alpha&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0037em&quot;&gt;α&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. The &lt;code&gt;update&lt;/code&gt; procedure follows these links to jump to the next insertion point instead of walking from the root every time.&lt;/li&gt;
&lt;li&gt;Finally, &lt;strong&gt;the ”$” terminator&lt;/strong&gt; converts an &lt;em&gt;implicit&lt;/em&gt; suffix tree, where some suffixes may end mid-edge, into an &lt;em&gt;explicit&lt;/em&gt; one where every suffix terminates at a distinct leaf.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;adding-more-strings&quot;&gt;Adding more strings&lt;/h2&gt;
&lt;p&gt;A generalized suffix tree indexes multiple strings. Each string is added with its own terminator, and the tree grows incrementally. Below, &lt;code&gt;panama&lt;/code&gt; is added after &lt;code&gt;banana&lt;/code&gt;. Step through and notice how much of the tree structure already exists from the first string.&lt;/p&gt;
&lt;div class=&quot;stv-root not-prose my-8&quot; data-stv-config=&quot;{&amp;#34;initialStrings&amp;#34;:[&amp;#34;banana&amp;#34;,&amp;#34;panama&amp;#34;],&amp;#34;initialSearch&amp;#34;:&amp;#34;&amp;#34;,&amp;#34;height&amp;#34;:550,&amp;#34;showBuilt&amp;#34;:false}&quot;&gt; &lt;!-- Playback + indexed strings --&gt; &lt;div class=&quot;stv-controls flex flex-wrap items-center gap-2 mb-2 p-3 rounded-lg border border-border-default bg-card&quot;&gt; &lt;div class=&quot;flex items-center gap-0.5 text-default&quot;&gt; &lt;button class=&quot;stv-reset p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Reset&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;19 20 9 12 19 4 19 20&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;5&quot; y1=&quot;19&quot; x2=&quot;5&quot; y2=&quot;5&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-prev p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Previous step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;11 19 2 12 11 5 11 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;22 19 13 12 22 5 22 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-play p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Play / Pause&quot;&gt; &lt;svg class=&quot;stv-play-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;svg class=&quot;stv-pause-icon hidden&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;rect x=&quot;6&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;rect x=&quot;14&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-next p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Next step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;13 19 22 12 13 5 13 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;2 19 11 12 2 5 2 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-end p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Skip to end&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;5 4 15 12 5 20 5 4&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;19&quot; y1=&quot;5&quot; x2=&quot;19&quot; y2=&quot;19&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;select class=&quot;stv-speed ml-1.5 px-1.5 py-1 text-xs border border-border-default rounded
                      bg-input text-default cursor-pointer&quot;&gt; &lt;option value=&quot;0.5&quot;&gt;0.5x&lt;/option&gt; &lt;option value=&quot;1&quot; selected&gt;1x&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;2x&lt;/option&gt; &lt;option value=&quot;4&quot;&gt;4x&lt;/option&gt; &lt;/select&gt; &lt;/div&gt; &lt;!-- Indexed strings tags (inline with playback) --&gt; &lt;div class=&quot;stv-strings flex flex-wrap items-center gap-1.5&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;!-- Add / Search --&gt; &lt;div class=&quot;flex flex-wrap items-center gap-2 mb-3&quot;&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-input flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Type a string...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-add px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Add
&lt;/button&gt; &lt;/div&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-search flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Search...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-search-btn px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Search
&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- SVG container + overlay prompt --&gt; &lt;div class=&quot;relative&quot; style=&quot;height: 550px&quot;&gt; &lt;div class=&quot;stv-canvas absolute inset-0 rounded-lg border border-border-default bg-card overflow-hidden&quot;&gt;&lt;/div&gt; &lt;!-- Empty state prompt (sits above the SVG) --&gt; &lt;div class=&quot;stv-prompt absolute inset-0 flex flex-col items-center justify-center text-muted pointer-events-none&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; class=&quot;opacity-40 mb-3&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;p class=&quot;stv-prompt-text text-sm&quot;&gt;Press play to watch the tree being built&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Step description --&gt; &lt;div class=&quot;stv-info mt-3 p-3 rounded-lg border border-border-default bg-card text-sm min-h-[3.5em]&quot;&gt; &lt;p class=&quot;stv-desc text-muted italic&quot;&gt;Add a string to begin building the suffix tree.&lt;/p&gt; &lt;div class=&quot;stv-progress mt-2 hidden&quot;&gt; &lt;div class=&quot;flex justify-between text-xs text-muted mb-1&quot;&gt; &lt;span class=&quot;stv-counter&quot;&gt;Step 0 / 0&lt;/span&gt; &lt;span class=&quot;stv-phase&quot;&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class=&quot;w-full bg-border-default rounded-full h-1&quot;&gt; &lt;div class=&quot;stv-bar bg-primary rounded-full h-1 transition-all duration-200&quot; style=&quot;width: 0%&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Length warning --&gt; &lt;div class=&quot;stv-warning hidden mt-2 p-2 rounded text-xs text-primary border border-primary/30 bg-primary/10&quot;&gt;
Long strings produce large trees that may be hard to read. Use zoom (scroll) and pan (drag) to navigate.
&lt;/div&gt; &lt;/div&gt; 
&lt;h2 id=&quot;searching&quot;&gt;Searching&lt;/h2&gt;
&lt;p&gt;Once the tree is built, searching for a pattern means matching characters along edges from the root. The visualization below has both strings pre-loaded. Try searching for &lt;code&gt;ana&lt;/code&gt;, then try &lt;code&gt;pan&lt;/code&gt;, &lt;code&gt;ban&lt;/code&gt;, &lt;code&gt;xyz&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;stv-root not-prose my-8&quot; data-stv-config=&quot;{&amp;#34;initialStrings&amp;#34;:[&amp;#34;banana&amp;#34;,&amp;#34;panama&amp;#34;],&amp;#34;initialSearch&amp;#34;:&amp;#34;ana&amp;#34;,&amp;#34;height&amp;#34;:550,&amp;#34;showBuilt&amp;#34;:true}&quot;&gt; &lt;!-- Playback + indexed strings --&gt; &lt;div class=&quot;stv-controls flex flex-wrap items-center gap-2 mb-2 p-3 rounded-lg border border-border-default bg-card&quot;&gt; &lt;div class=&quot;flex items-center gap-0.5 text-default&quot;&gt; &lt;button class=&quot;stv-reset p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Reset&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;19 20 9 12 19 4 19 20&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;5&quot; y1=&quot;19&quot; x2=&quot;5&quot; y2=&quot;5&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-prev p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Previous step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;11 19 2 12 11 5 11 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;22 19 13 12 22 5 22 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-play p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Play / Pause&quot;&gt; &lt;svg class=&quot;stv-play-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;svg class=&quot;stv-pause-icon hidden&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;rect x=&quot;6&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;rect x=&quot;14&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-next p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Next step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;13 19 22 12 13 5 13 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;2 19 11 12 2 5 2 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-end p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Skip to end&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;5 4 15 12 5 20 5 4&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;19&quot; y1=&quot;5&quot; x2=&quot;19&quot; y2=&quot;19&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;select class=&quot;stv-speed ml-1.5 px-1.5 py-1 text-xs border border-border-default rounded
                      bg-input text-default cursor-pointer&quot;&gt; &lt;option value=&quot;0.5&quot;&gt;0.5x&lt;/option&gt; &lt;option value=&quot;1&quot; selected&gt;1x&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;2x&lt;/option&gt; &lt;option value=&quot;4&quot;&gt;4x&lt;/option&gt; &lt;/select&gt; &lt;/div&gt; &lt;!-- Indexed strings tags (inline with playback) --&gt; &lt;div class=&quot;stv-strings flex flex-wrap items-center gap-1.5&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;!-- Add / Search --&gt; &lt;div class=&quot;flex flex-wrap items-center gap-2 mb-3&quot;&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-input flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Type a string...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-add px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Add
&lt;/button&gt; &lt;/div&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-search flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Search...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-search-btn px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Search
&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- SVG container + overlay prompt --&gt; &lt;div class=&quot;relative&quot; style=&quot;height: 550px&quot;&gt; &lt;div class=&quot;stv-canvas absolute inset-0 rounded-lg border border-border-default bg-card overflow-hidden&quot;&gt;&lt;/div&gt; &lt;!-- Empty state prompt (sits above the SVG) --&gt; &lt;div class=&quot;stv-prompt absolute inset-0 flex flex-col items-center justify-center text-muted pointer-events-none&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; class=&quot;opacity-40 mb-3&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;p class=&quot;stv-prompt-text text-sm&quot;&gt;Press play to watch the tree being built&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Step description --&gt; &lt;div class=&quot;stv-info mt-3 p-3 rounded-lg border border-border-default bg-card text-sm min-h-[3.5em]&quot;&gt; &lt;p class=&quot;stv-desc text-muted italic&quot;&gt;Add a string to begin building the suffix tree.&lt;/p&gt; &lt;div class=&quot;stv-progress mt-2 hidden&quot;&gt; &lt;div class=&quot;flex justify-between text-xs text-muted mb-1&quot;&gt; &lt;span class=&quot;stv-counter&quot;&gt;Step 0 / 0&lt;/span&gt; &lt;span class=&quot;stv-phase&quot;&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class=&quot;w-full bg-border-default rounded-full h-1&quot;&gt; &lt;div class=&quot;stv-bar bg-primary rounded-full h-1 transition-all duration-200&quot; style=&quot;width: 0%&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Length warning --&gt; &lt;div class=&quot;stv-warning hidden mt-2 p-2 rounded text-xs text-primary border border-primary/30 bg-primary/10&quot;&gt;
Long strings produce large trees that may be hard to read. Use zoom (scroll) and pan (drag) to navigate.
&lt;/div&gt; &lt;/div&gt; 
&lt;h2 id=&quot;try-it-yourself&quot;&gt;Try it yourself&lt;/h2&gt;
&lt;p&gt;An empty tree, yours to experiment with. Add strings, watch the construction, search for patterns. Use the scroll wheel to zoom and click-drag to pan if the tree gets large.&lt;/p&gt;
&lt;div class=&quot;stv-root not-prose my-8&quot; data-stv-config=&quot;{&amp;#34;initialStrings&amp;#34;:[],&amp;#34;initialSearch&amp;#34;:&amp;#34;&amp;#34;,&amp;#34;height&amp;#34;:550,&amp;#34;showBuilt&amp;#34;:false}&quot;&gt; &lt;!-- Playback + indexed strings --&gt; &lt;div class=&quot;stv-controls flex flex-wrap items-center gap-2 mb-2 p-3 rounded-lg border border-border-default bg-card&quot;&gt; &lt;div class=&quot;flex items-center gap-0.5 text-default&quot;&gt; &lt;button class=&quot;stv-reset p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Reset&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;19 20 9 12 19 4 19 20&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;5&quot; y1=&quot;19&quot; x2=&quot;5&quot; y2=&quot;5&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-prev p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Previous step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;11 19 2 12 11 5 11 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;22 19 13 12 22 5 22 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-play p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Play / Pause&quot;&gt; &lt;svg class=&quot;stv-play-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;svg class=&quot;stv-pause-icon hidden&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;18&quot; height=&quot;18&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot;&gt; &lt;rect x=&quot;6&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;rect x=&quot;14&quot; y=&quot;4&quot; width=&quot;4&quot; height=&quot;16&quot;&gt;&lt;/rect&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-next p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Next step&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;13 19 22 12 13 5 13 19&quot;&gt;&lt;/polygon&gt; &lt;polygon points=&quot;2 19 11 12 2 5 2 19&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;/button&gt; &lt;button class=&quot;stv-end p-1.5 rounded hover:bg-action-hover transition-colors&quot; title=&quot;Skip to end&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt; &lt;polygon points=&quot;5 4 15 12 5 20 5 4&quot;&gt;&lt;/polygon&gt; &lt;line x1=&quot;19&quot; y1=&quot;5&quot; x2=&quot;19&quot; y2=&quot;19&quot;&gt;&lt;/line&gt; &lt;/svg&gt; &lt;/button&gt; &lt;select class=&quot;stv-speed ml-1.5 px-1.5 py-1 text-xs border border-border-default rounded
                      bg-input text-default cursor-pointer&quot;&gt; &lt;option value=&quot;0.5&quot;&gt;0.5x&lt;/option&gt; &lt;option value=&quot;1&quot; selected&gt;1x&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;2x&lt;/option&gt; &lt;option value=&quot;4&quot;&gt;4x&lt;/option&gt; &lt;/select&gt; &lt;/div&gt; &lt;!-- Indexed strings tags (inline with playback) --&gt; &lt;div class=&quot;stv-strings flex flex-wrap items-center gap-1.5&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;!-- Add / Search --&gt; &lt;div class=&quot;flex flex-wrap items-center gap-2 mb-3&quot;&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-input flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Type a string...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-add px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Add
&lt;/button&gt; &lt;/div&gt; &lt;div class=&quot;flex items-center gap-1.5 flex-grow min-w-[180px]&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;stv-search flex-grow px-2.5 py-1.5 text-sm border border-border-default rounded
               bg-input text-default placeholder-muted
               focus:ring-2 focus:ring-primary focus:border-transparent outline-none&quot; placeholder=&quot;Search...&quot; maxlength=&quot;60&quot;&gt; &lt;button class=&quot;stv-search-btn px-3 py-1.5 text-sm font-medium rounded
               bg-primary text-on-primary hover:opacity-90
               transition-colors disabled:opacity-40 disabled:cursor-not-allowed&quot;&gt;
Search
&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- SVG container + overlay prompt --&gt; &lt;div class=&quot;relative&quot; style=&quot;height: 550px&quot;&gt; &lt;div class=&quot;stv-canvas absolute inset-0 rounded-lg border border-border-default bg-card overflow-hidden&quot;&gt;&lt;/div&gt; &lt;!-- Empty state prompt (sits above the SVG) --&gt; &lt;div class=&quot;stv-prompt absolute inset-0 flex flex-col items-center justify-center text-muted pointer-events-none&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; class=&quot;opacity-40 mb-3&quot;&gt; &lt;polygon points=&quot;5 3 19 12 5 21 5 3&quot;&gt;&lt;/polygon&gt; &lt;/svg&gt; &lt;p class=&quot;stv-prompt-text text-sm&quot;&gt;Press play to watch the tree being built&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Step description --&gt; &lt;div class=&quot;stv-info mt-3 p-3 rounded-lg border border-border-default bg-card text-sm min-h-[3.5em]&quot;&gt; &lt;p class=&quot;stv-desc text-muted italic&quot;&gt;Add a string to begin building the suffix tree.&lt;/p&gt; &lt;div class=&quot;stv-progress mt-2 hidden&quot;&gt; &lt;div class=&quot;flex justify-between text-xs text-muted mb-1&quot;&gt; &lt;span class=&quot;stv-counter&quot;&gt;Step 0 / 0&lt;/span&gt; &lt;span class=&quot;stv-phase&quot;&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class=&quot;w-full bg-border-default rounded-full h-1&quot;&gt; &lt;div class=&quot;stv-bar bg-primary rounded-full h-1 transition-all duration-200&quot; style=&quot;width: 0%&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;!-- Length warning --&gt; &lt;div class=&quot;stv-warning hidden mt-2 p-2 rounded text-xs text-primary border border-primary/30 bg-primary/10&quot;&gt;
Long strings produce large trees that may be hard to read. Use zoom (scroll) and pan (drag) to navigate.
&lt;/div&gt; &lt;/div&gt; 
&lt;h2 id=&quot;beyond-suffix-trees&quot;&gt;Beyond suffix trees&lt;/h2&gt;
&lt;p&gt;What excites me most is how well this generalizes. The gap between an algorithm on paper and an algorithm in memory has always been one of the hardest parts of learning computer science. Textbooks give you static diagrams. Debuggers give you one node at a time. Neither shows you the whole picture in motion.&lt;/p&gt;
&lt;p&gt;Browser-based rendering, interactive SVGs, and JavaScript engines fast enough to run non-trivial algorithms client-side make it possible to close that gap for almost any data structure. Red-black trees, B-trees, tries, skip lists, hash tables with open addressing: all of them would benefit from this kind of treatment. Not as a replacement for the theory, but as a companion to it. Read the algorithm, then &lt;em&gt;watch&lt;/em&gt; it work.&lt;/p&gt;
&lt;p&gt;There is an obvious question lurking here: why bother learning algorithms at all when you can ask an LLM to write one for you? I think the question misses the more interesting possibility. LLMs are not just code generators; they are learning accelerators. You can ask one to explain a single step of an algorithm, to walk through an edge case, or to generate a diagram of how components interact. When I started working in a new codebase recently, the fastest way for me to build a mental model was not reading code or documentation. It was asking an LLM to produce component and sequence diagrams: a much higher-bandwidth channel for understanding, at least for the way I think.&lt;/p&gt;
&lt;p&gt;That is the real shift. Not that machines can write algorithms so we don’t have to learn them, but that they can teach us in ways that adapt to how each of us actually learns. Through visualizations, through diagrams, through conversation, through whatever representation makes the concept click. This post is one example. The next one might look completely different, tailored to a different person and a different way of thinking.&lt;/p&gt;
&lt;p&gt;We write fewer algorithms from scratch in our day-to-day work than we used to. But we still benefit from understanding them, whether it’s to choose the right data structure, to debug performance issues, or to evaluate tradeoffs. And for those of us who enjoy algorithms for their own sake, the tools for learning them have never been better.&lt;/p&gt;
&lt;p&gt;The original Java suffix tree implementation is &lt;a href=&quot;https://github.com/abahgat/suffixtree&quot;&gt;open source on GitHub&lt;/a&gt;. For the full backstory, see the &lt;a href=&quot;https://www.abahgat.com/project/suffix-tree/&quot;&gt;project page&lt;/a&gt; and the &lt;a href=&quot;https://www.abahgat.com/blog/the-programming-puzzle-that-got-me-my-job/&quot;&gt;story of the programming puzzle&lt;/a&gt; that started it all. Ukkonen’s &lt;a href=&quot;https://www.cs.helsinki.fi/u/ukkonen/SuffixT1withFigs.pdf&quot;&gt;original paper&lt;/a&gt; remains the definitive reference for the algorithm.&lt;/p&gt;</content:encoded></item><item><title>The Velocity Paradox</title><link>https://www.abahgat.com/blog/the-velocity-paradox</link><guid isPermaLink="true">https://www.abahgat.com/blog/the-velocity-paradox</guid><description>AI agents can generate code 100x faster, but for companies stuck in the &quot;Unhappy Middle&quot; — with legacy debt, bespoke frameworks, and zero slack — the bottleneck has shifted from writing code to verifying it. Here&apos;s how engineering leaders can cross the chasm by becoming gardeners, not janitors.</description><pubDate>Mon, 23 Feb 2026 20:30:00 GMT</pubDate><content:encoded>&lt;p&gt;We’ve all been there. You sit down with an AI agent on a Saturday morning to hack on a side project and it feels like magic. Ten minutes in, you are blown away by how quickly the agent can turn even poorly organized thoughts into working prototypes. You feel like you could do this all day.&lt;/p&gt;
&lt;p&gt;And clearly, many of us do: we’re rediscovering our passion for side projects, and every day a thousand bespoke ToDo apps are born, perfectly tailored to the unique needs of their creators.&lt;/p&gt;
&lt;p&gt;At the same time, if you’re in an engineering leadership role, you’re also seeing your stakeholders dabble with agentic coding. They are shipping side-hustles on the weekend, and respectable work applications in an afternoon. Some of them might even look at you with ill-concealed suspicion. They want to know why their “pet feature” is stuck in a two-week cycle when they just whipped up a functional prototype over coffee.&lt;/p&gt;
&lt;p&gt;And they aren’t entirely wrong. AI agents have been writing 100% of my code for several months now. Informed by the wins on my side-projects, I wanted to see how much faster we could build at work. During the holiday break, I spent a few hours having Claude write a non-trivial feature that touched our database, cloud infra, mobile app, and the embedded application that runs on our hardware devices at &lt;a href=&quot;https://www.quilt.com&quot;&gt;Quilt&lt;/a&gt;. What would have taken me a week to write took an afternoon to generate.&lt;/p&gt;
&lt;p&gt;Yet it still took weeks to get it tested and merged.&lt;/p&gt;
&lt;p&gt;It felt like strapping a rocket engine to a tricycle. Exhilarating, sure, but the road ahead is still full of potholes, and there’s a canyon where the bridge used to be. So why isn’t the 100x improvement in how fast AI can &lt;em&gt;generate&lt;/em&gt; code moving the needle on how fast we can &lt;em&gt;ship&lt;/em&gt; features and improvements?&lt;/p&gt;
&lt;p&gt;Coding was never 100% of the job. But for those of us managing legacy debt, AI doesn’t just fail to solve our problems; it collides with them.&lt;/p&gt;
&lt;p&gt;I’ve been at several conferences recently where I met leaders from “AI-native” companies, organizations founded in an age where agentic coding is the baseline. One founder told me they don’t do code reviews at all; their CI pipeline is the reviewer. Another gives agents full control of their production infrastructure. For those of us anchored to a culture that is older than even just two years, these practices feel reckless. Yet even more measured companies are rethinking the fundamentals. OpenAI recently pulled back the curtain with their &lt;a href=&quot;https://openai.com/index/harness-engineering/&quot;&gt;Harness Engineering&lt;/a&gt; article, showing engineering re-architected around AI from the ground up.&lt;/p&gt;
&lt;p&gt;For the rest of us, the gap between “generating code” and “shipping value” is becoming a chasm. We are stuck in the Unhappy Middle, where the cost of code is diminishing rapidly, but the cost of review and verification is skyrocketing.&lt;/p&gt;
&lt;h1 id=&quot;the-unhappy-middle&quot;&gt;The Unhappy Middle&lt;/h1&gt;
&lt;p&gt;To understand why the promise of 100x faster progress thanks to AI still feels like an illusion, we have to look at the two forces we’re being squeezed by.&lt;/p&gt;
&lt;p&gt;On one side, we have the AI-Natives. These are companies and teams founded in the AI era. They have zero legacy debt, they can approach the craft of engineering with an open mind, and they use the same exact “boring” tech stacks the models were trained on. They don’t have to go out of their way to “integrate” AI; they are born out of it. They don’t have to refactor their code to support automated verification, they never knew a world without it.&lt;/p&gt;
&lt;p&gt;On the other side, you have the companies with the slack to reinvent themselves. Shopify’s CEO made headlines when he declared that &lt;a href=&quot;https://x.com/tobi/status/1909251946235437514&quot;&gt;AI proficiency is now a baseline expectation&lt;/a&gt; and that teams must justify why a job can’t be done by AI before requesting headcount. Companies like that (or Google, I bet) can dedicate teams to rearchitect their codebase, tooling and processes and build the scaffolding that is required to make AI work at scale.&lt;/p&gt;
&lt;p&gt;Then, there’s the rest of us. I call it the Unhappy Middle.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We support live products and services, with customers trusting us and depending on us daily. The cost of failure is higher than a toy prototype. Unlike your ToDo app, you can’t just throw an agent at a problem and hope it doesn’t break your production environment.&lt;/li&gt;
&lt;li&gt;We have accumulated technical debt as we were racing towards product/market fit, and yet never had the resources to pay it back. We have to balance work on infrastructure and developer experience with business priorities like opening new product lines. Most of these target ambitious schedules which (you guessed right) require taking on additional technical debt.&lt;/li&gt;
&lt;li&gt;With the age of Zero Interest-Rate Policies well behind us, but not quite with the coffers of a larger company, we always have to be mindful of our runway, are constantly short-staffed and always “do more with less”.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In short, we have to balance the technical complexity of an established company with the reality of a startup. Our survival depends on crossing the chasm as quickly as possible. Not every team is here. If your stack is standard and your tests are green, you may already be seeing the gains. But if any of this sounds familiar, the path forward is harder. Here are some examples from my reality.&lt;/p&gt;
&lt;h2 id=&quot;bespoke-frameworks-from-asset-to-dead-weight&quot;&gt;Bespoke Frameworks: from Asset to Dead Weight&lt;/h2&gt;
&lt;p&gt;Before AI, we may have optimized for human speed by building bespoke frameworks, custom boilerplate generators or domain-specific languages and abstractions. For many teams, these were their “secret sauce”: internal abstractions that helped teams move fast in 2022. They came at a price (typically, new engineers have to take some time getting comfortable with them), but they often paid off.&lt;/p&gt;
&lt;p&gt;Today, those clever optimizations are anchors holding us back. AI agents are brilliant at standard React and Python because they’ve seen it a billion times. And, at the same time, they are completely illiterate in our proprietary and opinionated internals. Every time I ask an agent to work in our bespoke code, I’m paying an invisible tax: I spend a third of my time fixing hallucinations because our “clever” code isn’t in anyone’s training set. (I wrote more about why this happens in &lt;a href=&quot;https://www.abahgat.com/blog/the-ghost-in-the-training-set&quot;&gt;The Ghost in the Training Set&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;And you know what’s funny? That’s often why some of the best engineers I know are unimpressed by AI agents: they focus on the last time they saw Claude trip on a gotcha that’s specific to their codebase and ignore the fact that it can build flawless React in the blink of an eye.&lt;/p&gt;
&lt;h2 id=&quot;zero-slack&quot;&gt;Zero Slack&lt;/h2&gt;
&lt;p&gt;We know technical debt is there, we always wanted to increase test coverage, we defer refactoring for testability because we need to fit one more feature before the release cut. We know that frameworks need to be standardized to become “AI-hospitable.” But in the Unhappy Middle, you have zero slack. You’re always racing, either to hit product-market fit or to extend your runway, and “cleaning up” feels like a luxury you can’t afford.&lt;/p&gt;
&lt;p&gt;This creates a painful tradeoff. In a side project, or a non-critical business app, failure is cheap. For a company with a legacy codebase, complex release processes and addressing user-critical needs, the stakes are considerably higher. Without the slack to build automated guardrails, we’re left with manual human review and auditing.&lt;/p&gt;
&lt;p&gt;And that’s where the 100x speed gain from AI goes to die.&lt;/p&gt;
&lt;h1 id=&quot;when-generation-outruns-verification&quot;&gt;When Generation Outruns Verification&lt;/h1&gt;
&lt;p&gt;We often think of the craft of software engineering as composed of several loops, each covering a different stage of the lifecycle, from idea to product. A good visual to illustrate this is the slide below, from a &lt;a href=&quot;https://youtu.be/qi89lhRI8zc?t=207&quot;&gt;talk Addy Osmani gave at LeadDev New York 2025&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;my-6&quot;&gt; &lt;img src=&quot;https://www.abahgat.com/_astro/devloop.CPhBLpoT.png&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot; width=&quot;1476&quot; height=&quot;820&quot; srcset=&quot;https://www.abahgat.com/_astro/devloop.CPhBLpoT_Z1g0ytw.webp 400w, /_astro/devloop.CPhBLpoT_NyH4h.webp 768w, /_astro/devloop.CPhBLpoT_2ggJxq.webp 1024w, /_astro/devloop.CPhBLpoT_2gfRf3.webp 1476w, /_astro/devloop.CPhBLpoT_1LXRS.webp 2040w, /_astro/devloop.CPhBLpoT_Z1tBj9K.webp 2952w&quot; sizes=&quot;(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px&quot; style=&quot;object-fit: cover; object-position: center; max-width: 1476px; max-height: 820px; aspect-ratio: 1.8; width: 100%;&quot; alt=&quot;The development loop&quot; class=&quot;mx-auto rounded-md shadow-lg bg-gray-400 dark:bg-slate-700 w-full&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt; &lt;figcaption class=&quot;mt-2 text-center text-sm text-gray-500 dark:text-muted&quot;&gt; From Addy Osmani&apos;s talk at LeadDev New York 2025 &lt;/figcaption&gt; &lt;/figure&gt;
&lt;p&gt;At the center is the &lt;em&gt;Inner Loop&lt;/em&gt;: the tight cycle of thinking, coding, building and testing. This is where “flow” happens. Surrounding that is the &lt;em&gt;Submit Loop&lt;/em&gt;, where your code goes through linting and code review, and the &lt;em&gt;Outer Loop&lt;/em&gt;, where it finally gets deployed and gets tested in the real world.&lt;/p&gt;
&lt;p&gt;The promise of AI-assisted engineering is to effectively collapse the Inner Loop. When an agent can “Think” and “Code” a cross-stack feature in a single morning, that center circle feels like it’s spinning at the speed of light.&lt;/p&gt;
&lt;p&gt;But for those of us who are still in the Unhappy Middle, that loop is often broken before it even starts.&lt;/p&gt;
&lt;h2 id=&quot;the-broken-inner-loop&quot;&gt;The Broken Inner Loop&lt;/h2&gt;
&lt;aside class=&quot;not-prose my-8 py-6 px-8 rounded-r-lg border-l-4 border-link bg-link/[0.07]&quot; role=&quot;note&quot; data-astro-cid-rssm4oil&gt; &lt;p class=&quot;text-xl md:text-2xl leading-relaxed text-heading italic pullquote-serif&quot; data-astro-cid-rssm4oil&gt; You were promised AI agents working for you. Instead, you are working for your agents. &lt;/p&gt; &lt;/aside&gt; 
&lt;p&gt;The first problem teams are likely to encounter is a broken Inner Loop. Before AI, back in the day when code was expensive to write, tests were the first aspect of a healthy architecture to be sacrificed (or, in the best case scenario, deferred). When we skip writing tests, it’s common to end up with code for which it’s hard to write tests in the long run.&lt;/p&gt;
&lt;p&gt;When you can’t give an agent a deterministic way to verify its own work, the feedback cycle doesn’t feed back into the AI, it feeds back into &lt;strong&gt;you&lt;/strong&gt;. The agent isn’t looping, it’s just throwing code over the wall and waiting for you to tell it what happened.&lt;/p&gt;
&lt;p&gt;In the best scenario you can imagine, the loop is closed by automation. The agent writes code, runs a test, sees the failure and iterates until it’s green. The feedback is a tight, self-correcting circuit.&lt;/p&gt;
&lt;p&gt;Without a way to automate verification, you’re just making a mountain of work for yourself, or accepting to take an enormous amount of risk by shipping code that hasn’t been properly tested.&lt;/p&gt;
&lt;p&gt;You were promised AI agents working for you to help you be more effective; instead, you are working for your agents. Not only is it not fun, it’s also a huge waste of your time because you are 100x slower than a software agent.&lt;/p&gt;
&lt;p&gt;In my world, this isn’t just a metaphor. I feel it physically. At Quilt, we make hardware devices, and you can’t throw prompt engineering at the physical world. If a test requires me to get up, walk to a test bench and manually press a button, the inner loop isn’t just broken; it’s wide open.&lt;/p&gt;
&lt;p&gt;And there are even worse consequences downstream.&lt;/p&gt;
&lt;h2 id=&quot;the-slowing-submit-loop&quot;&gt;The Slowing Submit Loop&lt;/h2&gt;
&lt;p&gt;Before AI agents were this capable, the high cost of &lt;em&gt;writing&lt;/em&gt; code carried a hidden benefit. If an engineer spent two days wrestling with a complex feature, they effectively distilled a lot of context information into their brain. By the time they put a change up for review, the author was the deepest expert on those 200 lines of code.&lt;/p&gt;
&lt;p&gt;That’s not how it works today.&lt;/p&gt;
&lt;p&gt;As wonderful as the democratizing effect of AI agents is (they enable engineers to contribute well beyond their historical area of expertise), it comes with downsides.&lt;/p&gt;
&lt;p&gt;If an agent can’t automatically verify its changes, and the author is not the most experienced engineer in the area affected by a change, the bulk of the burden of audit and review will shift to the reviewer.&lt;/p&gt;
&lt;p&gt;On the average team, code reviews are assigned to the most experienced engineers in a given area or domain. In this new world, these folks are getting overloaded with more code to review. Worse, they can no longer assume that the author has the same depth of knowledge about the code that reviewers historically could take for granted.&lt;/p&gt;
&lt;p&gt;At the extreme, this has multiple effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Because the agent did the heavy lifting, the human author may have a shallower understanding of the “why” behind specific implementation choices.&lt;/li&gt;
&lt;li&gt;The reviewer is now receiving 10x more code, but with 10x less intent provided by the author. If the reviewer didn’t (or couldn’t) do a thorough review themselves, it’s 10x more code reviews of a higher intensity. Think more of a forensic audit than a style check.&lt;/li&gt;
&lt;li&gt;In a legacy codebase with bespoke frameworks, this can be extremely challenging. If neither the author nor the reviewer fully understands the “clever” choices the AI made, they can’t distinguish between valuable additions and hallucinations, and therefore are taking a high risk shipping this to production.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The practical consequences are tangible. Code ends up spending more time waiting for review than in development (this is what happened to my proof of concept I mentioned earlier). Your most experienced engineers struggle to be productive themselves because they are drowning in code reviews.&lt;/p&gt;
&lt;p&gt;But the most worrisome part is what this does at an emotional level.&lt;/p&gt;
&lt;h1 id=&quot;from-craftspeople-to-janitors&quot;&gt;From Craftspeople to Janitors&lt;/h1&gt;
&lt;p&gt;If we take the patterns above to the extreme and let them fester without fixing them, then we are taking on a huge organizational risk by turning our most senior engineers into Janitors.&lt;/p&gt;
&lt;p&gt;Instead of going to a challenging workday where, at the end, we experience the joy of having created something new, we now have to pore over someone (or, rather, something) else’s code to spot issues and problems. Some engineers feel like they are being paid to clean up AI hallucinations.&lt;/p&gt;
&lt;p&gt;This can be deeply demotivating. No one likes being a linear bottleneck downstream of a stage that is accelerating at exponential speed. This is even more difficult at the speed this shift is happening, as many people are mourning the loss of the craft, made worse by simplistic takes about how the world of tomorrow needs fewer engineers.&lt;/p&gt;
&lt;p&gt;I still deeply enjoy coding but I recognize that, even in the best of days, a lot of the code I wrote was boilerplate needed to wire together different application components. A very common micro-kitchen joke from my time at Google was that we were all just highly-compensated Protocol Buffer translators.&lt;/p&gt;
&lt;p&gt;We miss the 20% of the code we used to write that was high-leverage and intellectually interesting, and forget the other 80% that was toilsome and repetitive.&lt;/p&gt;
&lt;h1 id=&quot;from-janitors-to-gardeners&quot;&gt;From Janitors to Gardeners&lt;/h1&gt;
&lt;p&gt;If you treat every AI-generated PR like a chore to be cleaned up, you are a Janitor. To move fast in a legacy codebase, we need a considerable change in mindset. If you allow me another metaphor, we need to start treating our codebase less like a perfect jewel to polish and more like a plot of land to tend to.&lt;/p&gt;
&lt;p&gt;I’ve been thinking about this metaphor for a while. As you scale an organization, you can’t afford to micromanage; you provide structure and support so that decisions happen organically, aligned to what the business needs. The same applies to codebases.&lt;/p&gt;
&lt;p&gt;Playing into the metaphor, a gardener may focus their attention on a few things:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tending the Soil&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hospitable Ground&lt;/strong&gt; — Transforming AI-Hostile codebases into an AI-Hospitable playing field requires investing in reducing technical debt, so that AI can’t hide behind it. It may mean moving away from bespoke patterns that routinely trip up agents, or making them work reliably. It means standardizing on a well-defined and documented set of abstractions, instead of having 3 different ways to set up an API server because we never finish migrations every time we deprecate an old pattern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nutrient-Rich Soil&lt;/strong&gt; — Agents are great at brute-forcing their way to a workable solution, but very often they struggle because the codebase lacks information beyond the code itself. Code written in haste often lacks documentation about “Intent” and the “Why” we made decisions. If we don’t expose context about tradeoffs and historical decisions, our agents are operating with limited information. Well structured &lt;code&gt;agents.md&lt;/code&gt; files are a good start. Checking in architectural guidelines and making them discoverable is increasingly paying off. Ironically, if you keep your design docs locked in Google Docs, your agent is blind to them (hey Google, when can we have MCP access to Google Docs?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Scaffolding and Direction&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scaffolding&lt;/strong&gt; — You don’t tell plants how to grow and expect them to listen; you provide scaffolding and support. In software, this can be types, interfaces and architectural boundaries. Well crafted designs that reduce coupling and abstract complexity behind well-defined interfaces are how you give agents a way to grow that is aligned to what you need.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resilience&lt;/strong&gt; — Automated tests, lint checks and verifications are much more helpful for AI agents than they are to humans, as they enable both faster iteration speed and more confidence in the review stage of the submit loop. In the gardening metaphor, this is akin to the sturdy fencing that protects your plants from critters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I find it ironic that many of the principles above are ones that practitioners have been advocating for under the banner of clean code, test-driven development and many others. We might callously shrug at the idea that we struggled to adopt them for the sake of our human co-workers and are now prioritizing them for the sake of our AI-agents. But the truth is that in the last decade, writing effective tests and good documentation cost us time: the time to think about them, and the time to type them. With AI agents being this capable, the typing cost is approaching zero. What remains is the thinking, and that was always the valuable part.&lt;/p&gt;
&lt;h1 id=&quot;building-the-dark-factory&quot;&gt;Building the Dark Factory&lt;/h1&gt;
&lt;aside class=&quot;not-prose my-8 py-6 px-8 rounded-r-lg border-l-4 border-link bg-link/[0.07]&quot; role=&quot;note&quot; data-astro-cid-rssm4oil&gt; &lt;p class=&quot;text-xl md:text-2xl leading-relaxed text-heading italic pullquote-serif&quot; data-astro-cid-rssm4oil&gt; Our job is no longer to write the code. It’s to build the factory that builds the code. &lt;/p&gt; &lt;/aside&gt; 
&lt;p&gt;By now, it should be obvious that if we use AI only to automate the “Coding” stage of the development loop, we may not only struggle to make our team more effective, we may even hurt their effectiveness.&lt;/p&gt;
&lt;p&gt;In the same talk by Addy Osmani I referenced earlier, he goes on to show several areas where AI can be effectively adopted to improve developer experience. In my day-to-day work, I’ve had considerable success using AI agents to troubleshoot bug reports and infrastructure alerts from our production fleet. The gains are real.&lt;/p&gt;
&lt;figure class=&quot;my-6&quot;&gt; &lt;img src=&quot;https://www.abahgat.com/_astro/devloop-annotated.DskcXKBh.png&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot; width=&quot;1570&quot; height=&quot;873&quot; srcset=&quot;https://www.abahgat.com/_astro/devloop-annotated.DskcXKBh_ZlcDUy.webp 400w, /_astro/devloop-annotated.DskcXKBh_20D2bA.webp 768w, /_astro/devloop-annotated.DskcXKBh_Z28MBIu.webp 1024w, /_astro/devloop-annotated.DskcXKBh_Z2wVHMf.webp 1570w, /_astro/devloop-annotated.DskcXKBh_Z2aAzMm.webp 2040w, /_astro/devloop-annotated.DskcXKBh_UUgiS.webp 3140w&quot; sizes=&quot;(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px&quot; style=&quot;object-fit: cover; object-position: center; max-width: 1570px; max-height: 873px; aspect-ratio: 1.7983963344788088; width: 100%;&quot; alt=&quot;The development loop, annotated&quot; class=&quot;mx-auto rounded-md shadow-lg bg-gray-400 dark:bg-slate-700 w-full&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt; &lt;figcaption class=&quot;mt-2 text-center text-sm text-gray-500 dark:text-muted&quot;&gt; From Addy Osmani&apos;s talk at LeadDev New York 2025 &lt;/figcaption&gt; &lt;/figure&gt;
&lt;p&gt;There is a growing conversation in engineering circles about “&lt;a href=&quot;https://www.danshapiro.com/blog/2026/01/the-five-levels-from-spicy-autocomplete-to-the-software-factory/&quot;&gt;Dark Factories&lt;/a&gt;”: fully automated systems that run without human intervention. In the age of AI, our job is no longer to write the code; it’s to &lt;strong&gt;build the factory that builds the code.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Some high-leverage areas to start:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Verification Machine&lt;/strong&gt; — Good test infrastructure should be the top priority. Well-written tests enable AI-agents to have much faster inner loops, but they also greatly help with faster code reviews. With good test scaffolding, you don’t just ask “Will this code work in this scenario?” You can ask an agent to demonstrate the expected behavior via a unit test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Address common tripping hazards for agents&lt;/strong&gt; — You likely have a few areas where agents routinely struggle. Don’t just scoff when that happens, and use it to say “AI isn’t quite there yet”. Ask yourself &lt;em&gt;why&lt;/em&gt; agents are struggling. Is it because of inconsistent patterns? Lack of context or documentation? Because your bespoke framework requires 1 year of experience &lt;em&gt;in your own codebase&lt;/em&gt; to master? Making sure agents don’t make the same mistake twice should be part of our responsibilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reducing human dependencies for mechanical tasks&lt;/strong&gt; — Invest in building reliable automated end to end tests that rely on production-like observability to spot issues and regressions. Wherever manual testing is required, ask yourself “what would it take for this test to happen automatically?” In a hardware company like Quilt, this means augmenting our ability to perform more tests in software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Lights-Out Goal&lt;/strong&gt; — Aim to have a “Submit Loop” so robust that if tests pass and the architectural boundaries are respected, the code is “shippable” by default. Even if that goal feels unrealistic (e.g. for code that is security-critical or that runs on devices that are hard to recover), ask yourself “What would it take for me to be 100% confident in a change without needing to review it?”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A word of warning: don’t confuse building the factory with building more features. If you ship 10x more features without correspondingly improving your infrastructure, you’re taking on a compounding liability. If AI agents today are enabling you to move even just a bit faster than yesterday, aim to put some of those velocity gains towards your scaffolding, instead of putting everything on more features.&lt;/p&gt;
&lt;h1 id=&quot;crossing-the-chasm&quot;&gt;Crossing the Chasm&lt;/h1&gt;
&lt;figure class=&quot;my-6&quot;&gt; &lt;img src=&quot;https://www.abahgat.com/_astro/chasm.r78RJefF.png&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot; width=&quot;3168&quot; height=&quot;1344&quot; srcset=&quot;https://www.abahgat.com/_astro/chasm.r78RJefF_Znct6D.webp 400w, /_astro/chasm.r78RJefF_Z1sM3lB.webp 768w, /_astro/chasm.r78RJefF_2f4awa.webp 1024w, /_astro/chasm.r78RJefF_Z1uMLRh.webp 2040w, /_astro/chasm.r78RJefF_ZTp7TR.webp 3168w, /_astro/chasm.r78RJefF_ZXFBLp.webp 6336w&quot; sizes=&quot;(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px&quot; style=&quot;object-fit: cover; object-position: center; max-width: 3168px; max-height: 1344px; aspect-ratio: 2.357142857142857; width: 100%;&quot; alt=&quot;A rocket-powered bicycle approaching a broken bridge over a canyon&quot; class=&quot;mx-auto rounded-md shadow-lg bg-gray-400 dark:bg-slate-700 w-full&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;  &lt;/figure&gt;
&lt;aside class=&quot;not-prose my-8 py-6 px-8 rounded-r-lg border-l-4 border-link bg-link/[0.07]&quot; role=&quot;note&quot; data-astro-cid-rssm4oil&gt; &lt;p class=&quot;text-xl md:text-2xl leading-relaxed text-heading italic pullquote-serif&quot; data-astro-cid-rssm4oil&gt; If the smartest AI in the world can’t understand your code, it might not be the AI’s fault. &lt;/p&gt; &lt;/aside&gt; 
&lt;p&gt;The Unhappy Middle is a trap, but it’s also an opportunity to rethink what engineering leadership looks like.&lt;/p&gt;
&lt;p&gt;This requires a fundamental shift in our ego as developers. Instead of ‘pwning’ the agent every time it trips on our proprietary abstractions, we need to ‘own’ our codebase and make it more AI-hospitable. If the smartest AI in the world can’t understand your code, it might not be the AI’s fault, but it might be a sign that our “cleverness” has become our biggest liability.&lt;/p&gt;
&lt;p&gt;If we don’t cross the chasm quickly and change our mindset about how we write software, we risk being buried under our own AI-generated slop. The first step is to stop prioritizing just features as our primary output and start prioritizing the speed and accuracy of the factory.&lt;/p&gt;
&lt;p&gt;It is notoriously hard to get organizational buy-in to address technical debt. The key is to reframe: this isn’t about “cleaning up” to pay off debt, it’s about investing in tooling to accomplish 10x velocity.&lt;/p&gt;
&lt;p&gt;And even then, there are harder questions ahead. If you actually succeed in building the “factory,” you’ll quickly find that the technical bottleneck has evaporated, only to leave you with an organizational one. A 10x software factory is effectively useless if it’s embedded in a 1x decision-making process. And it is possible that we are approaching a &lt;a href=&quot;https://en.wikipedia.org/wiki/Great_Filter&quot;&gt;Great Filter&lt;/a&gt;-like event for companies in the business of software — one that separates those who adapt from those who drown. But those are topics for another day.&lt;/p&gt;
&lt;p&gt;For now, the goal is clear: stop just auditing lines of code and start building the systems that define the future of our industry.&lt;/p&gt;
&lt;p&gt;Let us begin.&lt;/p&gt;</content:encoded></item><item><title>The Ghost in the Training Set</title><link>https://www.abahgat.com/blog/the-ghost-in-the-training-set</link><guid isPermaLink="true">https://www.abahgat.com/blog/the-ghost-in-the-training-set</guid><description>LLMs have statistical momentum: even when they know a new standard like Streamable HTTP exists, they often revert to the legacy patterns they saw most in training. Here is how to use &quot;strong anchors&quot; and &quot;zero-prompt pruning&quot; to keep your agentic systems from being haunted by 2024.</description><pubDate>Sat, 14 Feb 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the last several weeks, I’ve had to spend time setting up Model Context Protocol (MCP) servers. As the ecosystem matures, it is already navigating its first major paradigm shifts. Specifically, in early 2025, the recommended transport for MCP over HTTP shifted from Server-Sent Events (SSE) to &lt;a href=&quot;https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http&quot;&gt;&lt;strong&gt;Streamable HTTP&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To my surprise, the agents I use most (Gemini and Claude) kept reverting to SSE. They were well “aware”, at least as much as a machine could be, that Streamable HTTP was the new standard (they could competently answer questions about it) but they were haunted by the &lt;strong&gt;statistical momentum&lt;/strong&gt; of their own training data. When it came time to actually generate code, they defaulted to the pattern they had seen thousands of times before.&lt;/p&gt;
&lt;h2 id=&quot;the-invisible-weight-of-training-bias&quot;&gt;The Invisible Weight of Training Bias&lt;/h2&gt;
&lt;p&gt;Taking a step back, this makes perfect sense: LLMs don’t just “read” instructions in a traditional sense: they weigh them against their internal probability map. If most of the MCP implementations they had seen were built over SSE, that gives them a huge bias in that direction.&lt;/p&gt;
&lt;p&gt;Once I started noticing this pattern, I had found it more and more often: LLMs seem to struggle more with bleeding edge patterns and technologies (again, their training dataset has more examples built on deprecated patterns than newer standards).&lt;/p&gt;
&lt;p&gt;This is a sneaky pattern, because we don’t naturally think about how old (or new) a model’s training set is, so we can’t realize this is happening unless we pay attention. If you’re working on a bleeding edge domain and you’re not careful, you may find yourself with an agent offering you a beautiful implementation that is actually a frozen snapshot of &lt;em&gt;last year&lt;/em&gt;’s best practices.&lt;/p&gt;
&lt;p&gt;The challenge grows with the uniqueness of your environment. This problem is even worse with codebases that adopt bespoke frameworks and patterns for which there is no published precedent. Agents thrive on &lt;em&gt;Common Knowledge&lt;/em&gt;, and they struggle with &lt;em&gt;Private Context&lt;/em&gt;. When we use bespoke patterns, we are essentially moving the agent into a zero-shot environment without even realizing it. The result is a performance degradation that looks like a “dumb” model but it is actually a lack of statistical grounding.&lt;/p&gt;
&lt;h2 id=&quot;from-prompting-to-infrastructure&quot;&gt;From Prompting to Infrastructure&lt;/h2&gt;
&lt;p&gt;You may be tempted to try to overcome this through prompting, and try to give strong instructions to anchor your agent towards the new standard by including strong language in your prompt (&lt;code&gt;ALWAYS use Streamable HTTP when implementing MCP services&lt;/code&gt;). You need strong anchors to overcome strong biases. But prompts are often lossy, inconsistent and error-prone.&lt;/p&gt;
&lt;p&gt;A more sustainable strategy is to start including these guardrails into your &lt;code&gt;agents.md&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; files, or even better in tooling infrastructure. For example, Claude includes an &lt;code&gt;/mcp-builder&lt;/code&gt; skill, which serves as a specialized instruction package anchored on the most recent standards, ensuring you land with a well-functioning implementation that overcomes the inherent bias in the models. In contrast, if you tried building an MCP server with Gemini now you may find yourself surprised by a perfectly functional implementation built on the deprecated 2024 pattern.&lt;/p&gt;
&lt;h2 id=&quot;the-trap-of-contextual-debt&quot;&gt;The Trap of “Contextual Debt”&lt;/h2&gt;
&lt;p&gt;Just like code accumulates technical debt, continuously adding to &lt;code&gt;agents.md&lt;/code&gt; without ever cleaning up leads to “contextual debt”. Over time, these files become bloated with a mountain of “Don’t do X” or “Remember Y.” Even worse, because you can have &lt;code&gt;agents.md&lt;/code&gt; files scattered through your repo, and other &lt;code&gt;.md&lt;/code&gt; files as documentation, you can find yourself with clashing instructions that throw agents for a loop in ways that are surprisingly difficult to detect and remedy.&lt;/p&gt;
&lt;p&gt;We are reaching a point where our “Instruction Budget” is as important as our compute budget. If you have clashing instructions across multiple &lt;code&gt;.md&lt;/code&gt; files, you’re not just wasting tokens, you’re creating “hallucination traps” that are far more expensive to debug than a standard syntax error.&lt;/p&gt;
&lt;p&gt;Here are a few things that worked well for me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Progressive Disclosure&lt;/strong&gt;: Borrowing from the &lt;a href=&quot;https://resources.anthropic.com/hubfs/The-Complete-Guide-to-Building-Skill-for-Claude.pdf&quot;&gt;Claude skills playbook&lt;/a&gt;, instead of having a giant instruction file, use a modular approach (e.g., a &lt;code&gt;docs/MCP_STANDARDS.md&lt;/code&gt; file linked from your root &lt;code&gt;agents.md&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The “Zero-Prompt Test” Stress Test&lt;/strong&gt;: Periodically run an agent on your project with a blank instruction file (especially after significant model updates). If performance remains stable, the underlying training set has likely caught up to the new standard. At that point, your manual instructions are no longer necessary; they are cruft. Delete them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ownership of Configs&lt;/strong&gt;: Treat agent configurations with as much rigor as a CI/CD pipeline. Obsolete agent instructions have even more impact on your velocity than obsolete documentation, and ironically, up-to-date documentation is now more precious than ever.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the rapid pace at which things are evolving, I would not be surprised if in a year, half of these strategies would not be necessary as agents get better. And perhaps they will be superseded by a new set of practices.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-managing-the-agents-ai-memory&quot;&gt;Conclusion: Managing the Agent’s AI “Memory”&lt;/h2&gt;
&lt;p&gt;Regardless of what you might think about the tropes around “software engineering being dead”, it is undeniable that the focus of our job is moving away (or perhaps upward) from writing code.&lt;/p&gt;
&lt;p&gt;As we spend more effort managing attention and memory of our agents, in the most sustainable agentic systems, instructions and scaffolding will be pruned as ruthlessly, if not more so, than the code itself.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;In this post, we’ll reference only &lt;code&gt;agents.md&lt;/code&gt;. Hopefully we’re not far from the day where we don’t need to maintain a separate configuration for Claude. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Receiving Feedback Is A Skill</title><link>https://www.abahgat.com/blog/receiving-feedback</link><guid isPermaLink="true">https://www.abahgat.com/blog/receiving-feedback</guid><description>Delivering feedback is a critical part of my day job as a manager at Google. However, it took me a while to realize that receiving feedback is one of the skills that helped me grow the most in my career. Here a few things I learned in the process.</description><pubDate>Tue, 25 Aug 2020 19:35:24 GMT</pubDate><content:encoded>&lt;p&gt;Delivering feedback is a critical part of my day job as a manager at Google. However, it took me a while to realize that &lt;em&gt;receiving&lt;/em&gt; feedback is one of the skills that helped me grow the most in my career.&lt;/p&gt;
&lt;p&gt;For many of us, our job is the first setting where we receive developmental feedback from people other than our parents or teachers. That experience may be quite shocking.&lt;/p&gt;
&lt;p&gt;I still remember the first time I got professional feedback early in my career. I remember almost every single word that my manager chose to use.&lt;/p&gt;
&lt;p&gt;What I remember even more vividly though is the strong reaction that feedback caused in me. Within seconds, I got defensive, I felt like I was being criticized, attacked, unappreciated. I heard what they were trying to tell me, but something inside me kept translating that into a personal criticism. A statement about how I, personally, fell short of expectations.&lt;/p&gt;
&lt;p&gt;Good feedback sounds like “here’s one thing you can do better next time”. Better feedback sounds like “here’s one thing that you could do differently to achieve a greater result”.&lt;/p&gt;
&lt;p&gt;Embracing that mindset allowed me to accept, process and build on feedback. While I can’t say I &lt;em&gt;prefer&lt;/em&gt; criticism over praise, constructive feedback no longer makes me uncomfortable. Instead, I actively seek it.&lt;/p&gt;
&lt;p&gt;Changing my mindset around feedback required me to make two key changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stop doing things that hurt my ability to improve&lt;/li&gt;
&lt;li&gt;start doing things that help build on what I hear&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;things-i-stopped-doing&quot;&gt;Things I Stopped Doing&lt;/h2&gt;
&lt;h3 id=&quot;taking-it-personally&quot;&gt;Taking It Personally&lt;/h3&gt;
&lt;p&gt;The main reason I had a difficult time processing feedback is the fact that I often took it personally.&lt;/p&gt;
&lt;p&gt;When receiving feedback about something I did, I often read it as feedback about &lt;em&gt;me&lt;/em&gt;. Oftentimes, that was not the intention.&lt;/p&gt;
&lt;p&gt;Instead of hearing “this email was hard to understand”, I heard “you do not communicate effectively”. When the other party was saying “this piece of code is brittle”, I was hearing “you are a lousy programmer”.&lt;/p&gt;
&lt;p&gt;I often ended up reacting defensively. I was unable to hear and processing the actual message I needed to receive.&lt;/p&gt;
&lt;p&gt;Most developmental feedback will naturally trigger a defensive attitude. That prevents us from getting the full value of what the other person is trying to tell us. &lt;em&gt;We need to make a conscious effort to not jump to defensive mode, and rather engage in active listening&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;arguing-with-feedback&quot;&gt;Arguing With Feedback&lt;/h3&gt;
&lt;p&gt;Even worse than taking feedback personally, I sometimes found myself wanting to argue with the person delivering it. I wanted to explain why I disagreed with what they were seeing or try to convince them that they were wrong.&lt;/p&gt;
&lt;p&gt;In most cases, arguing with feedback is pointless. Take an example from many years ago.&lt;/p&gt;
&lt;p&gt;A colleague approached me and told me “I think the comments you left in this review were too harsh”.&lt;/p&gt;
&lt;p&gt;Now, if they cared enough to bring up this feedback, perhaps they were not the only ones. Or maybe my communication style could have had an unintended effect on some people, some time.&lt;/p&gt;
&lt;p&gt;Yes, I could have argued with my colleague, perhaps even convince them that my tone was not that bad. Winning the argument might even have felt better.&lt;/p&gt;
&lt;p&gt;That would not have changed the my comments did trigger a negative reaction for them. Quite likely, others might have had the same reaction. Knowing that, having that awareness, made me more thoughtful when writing review comments. I can tell they were better received from that moment on.&lt;/p&gt;
&lt;p&gt;Arguing with people who are trying to give us feedback, does not help us. Eventually, people will shy away from telling us where we can improve. It leads us to us working with less information about what we can do to get better. In the long run, we miss out on a significant opportunity.&lt;/p&gt;
&lt;h2 id=&quot;things-i-learned-to-do-instead&quot;&gt;Things I Learned To Do Instead&lt;/h2&gt;
&lt;h3 id=&quot;being-thankful&quot;&gt;Being Thankful&lt;/h3&gt;
&lt;p&gt;A friend of mine once shared a quote that sounded like “feedback is a gift”&lt;/p&gt;
&lt;p&gt;Good feedback is thoughtful and timely. Often, it is as difficult to deliver as it is to receive. It is especially difficult for people we are not very close with.&lt;/p&gt;
&lt;p&gt;Any yet, some people choose to take a risk. They let us know where we can do better. They do that knowing well that we may feel hurt by what they say.&lt;/p&gt;
&lt;p&gt;Because of this, the first thing I do when receiving feedback is thank whoever is giving it. I thank them because they took a risk and did something uncomfortable. I also thank them because what they are telling me has the potential of making me much better.&lt;/p&gt;
&lt;p&gt;Good feedback allows us to identify growth areas. Areas where we could invest more to get better at something we have been trying to do. Even those of us that have good self-awareness often need to work hard to find where they need to improve the most.&lt;/p&gt;
&lt;p&gt;If someone is coming to us with feedback, they may be sparing us a lot of hard work required to identify areas of improvement.&lt;/p&gt;
&lt;p&gt;The least we can do is thank them profusely for the gift they just gave us and get to work.&lt;/p&gt;
&lt;h3 id=&quot;following-up&quot;&gt;Following Up&lt;/h3&gt;
&lt;p&gt;Whenever I receive feedback about something I can improve and want to work on, I note it down. Over time, this list becomes my feedback log.&lt;/p&gt;
&lt;p&gt;Keeping a list of the items I am trying to get better at is a way to hold myself accountable. I go through this feedback log every few weeks and reflect on the progress (or lack of progress) I have seen so far.&lt;/p&gt;
&lt;p&gt;This helps me making sure I make the most of the feedback I was generously given and use it to gradually get better. I try to spend some time every week to work on some of the most important items on the feedback log.&lt;/p&gt;
&lt;p&gt;Doing this helps me well beyond the result of addressing feedback. It also helps me ground my identity as someone who can accept feedback gracefully and use it as a tool to keep growing every day.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;A few simple changes in perspective helped me change my view on feedback. I went from seeing it as a threat to my own self-worth to a stepping stone to become a better version of myself.&lt;/p&gt;
&lt;p&gt;The results of this attitude compound over time as I keep focusing my energy towards addressing the most critical feedback items.&lt;/p&gt;</content:encoded></item><item><title>Programming Machine Learning</title><link>https://www.abahgat.com/blog/programming-machine-learning</link><guid isPermaLink="true">https://www.abahgat.com/blog/programming-machine-learning</guid><description>A book written with developers in mind, covering Machine Learning with a hands-on approach. Each new topic is introduced by laying out a real world problem, guiding readers through implementing a working solution based on ML algorithms and then explaining the theoretical foundations in a very accessible way.</description><pubDate>Mon, 04 May 2020 14:47:47 GMT</pubDate><content:encoded>&lt;p&gt;I just received my copy of &lt;a href=&quot;https://amzn.to/3bUyn5a&quot;&gt;Programming Machine Learning&lt;/a&gt;, a book by &lt;a href=&quot;https://twitter.com/nusco&quot;&gt;Paolo Perrotta&lt;/a&gt;. I had the pleasure of being one of the technical reviewers of the draft and, while this is not the first book I read about Machine Learning, I must say it became one of my favorites.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.abahgat.com/_astro/programming-ml.yXEqwxlW_1oikSk.webp&quot; alt=&quot;The book cover&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;300&quot; height=&quot;360&quot;&gt;&lt;/p&gt;
&lt;p&gt;Paolo promises, at the beginning of the book, to write a book meant for developers, and he delivers on that promise.&lt;/p&gt;
&lt;p&gt;In his words,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the book I missed when I got started with machine learning: &lt;mark&gt;an introduction for developers, written in our own language&lt;/mark&gt;. After reading it, you’ll be comfortable with the fundamentals, and able to write machine learning programs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Programming Machine Learning&lt;/em&gt; is a book that teaches the foundations of ML by walking the reader through the process of implementing working solutions for a few concrete and specific use cases, such as predicting sales volume for a pizzeria, recognizing hand-written digits or classifying images.&lt;/p&gt;
&lt;p&gt;Each chapter introduces a challenge, lays out the foundations of a technical implementation and explains the theoretical background behind the techniques adopted.&lt;/p&gt;
&lt;p&gt;As a result, the book is much easier to follow than many others on this subject: even when diving deeper into the technical or mathematical aspects of any of the topics covered, the reader is able to build on the empirical intuition that comes from having implemented ML algorithms and having seen them in action. Every chapter is engaging, starting from the first ones, about trying to predict pizza sales via linear regression and simple perceptrons, to the last ones, leveraging &lt;a href=&quot;https://keras.io/&quot;&gt;Keras&lt;/a&gt; to classify images.&lt;/p&gt;
&lt;p&gt;I found the overall approach quite novel and refreshing. I would definitely recommend &lt;em&gt;Programming Machine Learning&lt;/em&gt;, especially if you are the type of engineer who generally enjoys learning by doing.&lt;/p&gt;</content:encoded></item><item><title>The programming puzzle that landed me my job</title><link>https://www.abahgat.com/blog/the-programming-puzzle-that-got-me-my-job</link><guid isPermaLink="true">https://www.abahgat.com/blog/the-programming-puzzle-that-got-me-my-job</guid><description>And how solving it required a truly full-stack solution, covering web development, data structures and memory optimization</description><pubDate>Tue, 01 Oct 2019 03:35:33 GMT</pubDate></item><item><title>What to look for when hiring</title><link>https://www.abahgat.com/blog/what-to-look-for-when-hiring</link><guid isPermaLink="true">https://www.abahgat.com/blog/what-to-look-for-when-hiring</guid><description>A while ago, I found myself in the enviable position of having to rapidly grow my team. Here a list of the most important characteristics I learned to value in anyone I work with, regardless of job function.</description><pubDate>Mon, 26 Aug 2019 19:09:10 GMT</pubDate><content:encoded>&lt;p&gt;A while ago, I found myself in the enviable position of having to rapidly grow my team. By then, I had done a large number of technical interviews, so I had an idea of what to look for in strong candidates for Software Engineering positions. However, I felt like I lacked a framework for understanding how likely a given candidate was to succeed if they had joined my team, beyond a very loose definition of “culture fit”.&lt;/p&gt;
&lt;p&gt;As I was trying to better understand what I was looking for, I started to think about what I value in the people I work with and to reflect on traits I found to be quite common among some of the most successful people I have worked with over the course of my career.&lt;/p&gt;
&lt;p&gt;While I would not expect every person I work with to exhibit &lt;em&gt;all&lt;/em&gt; the qualities I list here, I am always positively impressed when I come across someone who exhibits more than a few and equally concerned when I see no hint of any of these characteristics.&lt;/p&gt;
&lt;p&gt;Over time, I became quite sensitive to some hints that suggest someone could possess one of the these traits and I learned to probe further whenever I see them.&lt;/p&gt;
&lt;p&gt;Here a list of the most important characteristics I learned to value in anyone I work with, regardless of job function.&lt;/p&gt;
&lt;h2 id=&quot;intrinsic-motivation&quot;&gt;Intrinsic Motivation&lt;/h2&gt;
&lt;p&gt;Many of the best people I worked with are motivated by their own desire to improve, regardless of the environment around them. Certainly, having a great team and a lot of attention from their manager will help them as well as it would help anyone else, but being intrinsically motivated means they are able to find satisfaction without relying on artificial nudges from the system around them.&lt;/p&gt;
&lt;p&gt;I tend to enjoy working with people who think this way because they are often pushing themselves to get better every day, react better to difficulties and challenges and, as a result, push me to get better as well.&lt;/p&gt;
&lt;p&gt;I know I am looking at someone who has this kind of attitude when they show they are driven by things such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;learning something new every day&lt;/li&gt;
&lt;li&gt;mastering a skill or a craft&lt;/li&gt;
&lt;li&gt;accomplishing something they thought of as difficult&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having hobbies and non-trivial side-projects (for those of us who are at a point where they can afford the time required) is often a sign of being intrinsically motivated.&lt;/p&gt;
&lt;h2 id=&quot;relentless-focus&quot;&gt;Relentless Focus&lt;/h2&gt;
&lt;p&gt;Success often requires from focusing on the most important things first and almost ignoring everything else.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Effective executives concentrate on the few major areas where superior performance will produce outstanding results. They force themselves to set priorities and stay with their priority decisions. They know that they have no choice but to do first things first—and second things not at all. The alternative is to get nothing done. &lt;span class=&quot;blockquote-footer&quot;&gt;Peter F. Drucker, &lt;a href=&quot;https://amzn.to/2ZndZml&quot;&gt;&lt;cite title=&quot;The Effective Executive&quot;&gt;The Effective Executive&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I found it hard to gauge how good anyone is at focusing on top priorities based solely on casual conversations. One decent proxy, at least for technical roles, are open ended system design interviews. Many good questions involve asking to solve a problem too large to be tackled within the allotted time or with the given constraints. That forces the candidate to narrow down the scope and focus on the most important aspects of the problem and set everything else aside.&lt;/p&gt;
&lt;h2 id=&quot;independent-thinking&quot;&gt;Independent Thinking&lt;/h2&gt;
&lt;p&gt;A couple of the best people I worked with have a way of asking questions that sometimes can come across as blunt or excessively direct. In their case, I have never had a problem with it, since it is tied to what I believe to be one of their strengths: they are not afraid to question a line of thought if they do not fully understand it or if they disagree with it.&lt;/p&gt;
&lt;p&gt;In cultures where it is more comfortable to agree with others than to challenge their thinking, it takes courage to express dissent.&lt;/p&gt;
&lt;p&gt;I &lt;a href=&quot;https://www.abahgat.com/post/2012-03-12-building-a-culture-of-objection/index.md&quot;&gt;wrote before&lt;/a&gt; how much I value a culture where anyone feels free to voice their disagreement: I value even more individuals who are comfortable speaking up regardless of what the environment surrounding them looks like.&lt;/p&gt;
&lt;p&gt;This is another trait that is be hard to spot in casual conversations, I have seen this come across as a set of pointed, specific questions aimed at developing a stronger understanding of a topic and then thoughtfully suggesting there might be a different way to approach a problem.&lt;/p&gt;
&lt;p&gt;However, there is a fine line between being willing to challenge ideas when they are not rock solid and being contrarian by default: it is hard to work with someone who disagrees with everything on principle.&lt;/p&gt;
&lt;h2 id=&quot;fast-learning&quot;&gt;Fast Learning&lt;/h2&gt;
&lt;p&gt;The ability to learn quickly and adapt to changing circumstances is one of the most critical skills to have in this day and age. To me, it means that I can trust someone to be able to be asked to do something they have not done before and rapidly get up to speed.&lt;/p&gt;
&lt;p&gt;I generally see this through evidence of high rate of improvement; whether it shows as gaining mastery of many technologies in a short time, working across a number of different domains or being promoted repeatedly while at the same company, this shows an ability to adapt to changing circumstances.&lt;/p&gt;
&lt;h2 id=&quot;responsiveness-and-follow-through&quot;&gt;Responsiveness and Follow Through&lt;/h2&gt;
&lt;p&gt;One of the main differences between working with a team and working by ourselves is that when we are part of a team others tend to depend on our output for their own progress.&lt;/p&gt;
&lt;p&gt;Oftentimes, managers end being stuck having to play the role of the persistent nag, reminding others of their prior commitments and making sure that any work that was agreed upon is eventually delivered. Clearly, this is a way around a fairly common problem: the average person is not great at following through.&lt;/p&gt;
&lt;p&gt;By contrast, the most effective team players I have worked with hardly need any nudges: they will stay on top of their to-do list and consistently deliver anything they agreed to do by the time they said they would, without you ever needing to ask again.
If you do ask something of them, they respond right away.&lt;/p&gt;
&lt;p&gt;Sadly, I do not know of a way to assess how well anyone would do on this point without speaking to anyone who has worked with them before.&lt;/p&gt;
&lt;h2 id=&quot;decisiveness&quot;&gt;Decisiveness&lt;/h2&gt;
&lt;p&gt;Many people struggle with decisions, for fear of making a mistake, being proven wrong and fallible or committing to the wrong direction. Whatever the reason, shying away from decisions is rarely helpful.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“In effect, the lack of a decision is the same as a negative decision; no green light is a red light, and work can stop for a whole organization.” &lt;span class=&quot;blockquote-footer&quot;&gt;Andrew S. Grove, &lt;a href=&quot;https://amzn.to/32dlLB9&quot;&gt;&lt;cite title=&quot;High Output Management&quot;&gt;High Output Management&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The truth is that many decisions are relatively easy to reverse if necessary but the cost of paralysis is too high for most teams and organizations to afford. High-stakes decisions are rare, but when facing one it is important to treat it as a priority and not linger too long. The worst thing we can do is simply dwell on it and get stuck.&lt;/p&gt;
&lt;p&gt;Decisiveness is often the driving force behind the &lt;a href=&quot;#responsiveness-and-follow-through&quot;&gt;responsiveness&lt;/a&gt; in the previous section.&lt;/p&gt;
&lt;h2 id=&quot;curiosity-and-inquisitiveness&quot;&gt;Curiosity and Inquisitiveness&lt;/h2&gt;
&lt;p&gt;Beyond being a &lt;a href=&quot;#fast-learning&quot;&gt;fast learner&lt;/a&gt; or being passionate about the specifics of someone’s own job, being curious and inquisitive can be invaluable in understanding one’s own teammates, manager, users and competitors.&lt;/p&gt;
&lt;p&gt;By wondering about the “why” behind anything we observe, we develop a stronger understanding of the problem we are trying to solve or the parties and organizations we are working with. An understanding that inevitably helps us be more effective.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“When you get curious and learn how to turn that disagreement into honest questioning, you can learn more about other perspectives on the issue because your team will open up.”
&lt;span class=&quot;blockquote-footer&quot;&gt;Camille Fournier, &lt;a href=&quot;https://amzn.to/2KY97jH&quot;&gt;&lt;cite title=&quot;The Manager&amp;#x27;s Path&quot;&gt;The Manager’s Path&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oddly enough, at least based on my own experience, it is fairly common to find engineers who are extremely curious about technical topics but tend to be less interested about understanding less technical subjects (such as organizations and other humans). People I worked with who are truly inquisitive tend to demonstrate it by being uncommonly interested in the motivation behind the status quo or previous decisions. They often ask questions such as “Why do we do things this way?”&lt;/p&gt;
&lt;h2 id=&quot;communication&quot;&gt;Communication&lt;/h2&gt;
&lt;p&gt;So much of teamwork is communication, yet communication skills are often overlooked. It is hard to overstate the importance of communication in teamwork. Effective communication means, among other things,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;being able to make one’s point of view understood,&lt;/li&gt;
&lt;li&gt;resolving conflicts,&lt;/li&gt;
&lt;li&gt;selling our own vision,&lt;/li&gt;
&lt;li&gt;making sure others are aware of our work (and why it matters)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of all the traits I learned to appreciate, this is perhaps the most visible. If you spend even a few minutes speaking with someone and they are an effective communicator, you will notice.&lt;/p&gt;
&lt;h2 id=&quot;going-the-extra-mile&quot;&gt;Going the Extra Mile&lt;/h2&gt;
&lt;p&gt;Many successful people consistently overdeliver. It is quite difficult to have any sort of success by just doing the bare minimum. Sure, one can get lucky once or twice, but solid careers are built on strings of consistent achievements.&lt;/p&gt;
&lt;p&gt;I often see this in coming through from people’s passions. It often shows as side projects (work-like activities they chose to do in their own time&lt;sup&gt;&lt;a href=&quot;#user-content-fn-side-projects&quot; id=&quot;user-content-fnref-side-projects&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;) or initiatives at work that they started without anyone asking them to do so (e.g. 20% projects at Google).&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-side-projects&quot;&gt;
&lt;p&gt;Note that this is not always possible for people to do, depending on their situation. &lt;a href=&quot;#user-content-fnref-side-projects&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Visual and HTML Testing for Static Sites</title><link>https://www.abahgat.com/blog/testing-static-sites</link><guid isPermaLink="true">https://www.abahgat.com/blog/testing-static-sites</guid><description>I set up a CI/CD pipeline to test my website for markup and rendering issues. It proved to be so useful that I can not imagine going back.</description><pubDate>Tue, 06 Aug 2019 11:06:27 GMT</pubDate></item><item><title>Zing LED Smart Night Light</title><link>https://www.abahgat.com/blog/zing-night-light</link><guid isPermaLink="true">https://www.abahgat.com/blog/zing-night-light</guid><description>I liked these WiFi enabled, motion-sensing night lights so far, I only wish they had 3 more features.</description><pubDate>Mon, 18 Feb 2019 21:24:08 GMT</pubDate><content:encoded>&lt;p&gt;Several months ago I was looking for a night light when I stumbled upon &lt;a href=&quot;https://www.indiegogo.com/projects/zing-smart-night-light#/&quot;&gt;Zing’s Indiegogo page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main feature I was looking for was for the light to activate automatically when I was walking past it and to turn off a few seconds later. Zing seemed to be able to do this and more: after seeing browsing the site, what intrigued me were the many possibilities for customization, the integration with &lt;a href=&quot;https://www.ifttt.com&quot;&gt;IFTTT&lt;/a&gt; and the fact that each light has a temperature sensor — which I was hoping I would eventually be able to access via API.&lt;/p&gt;
&lt;p&gt;Some features, such as automatic path lighting and the locator feature, were not a part of the decision.
Others, event notification in particular, I knew I would not use (I am trying to minimize the notifications I get while I am home).&lt;/p&gt;
&lt;p&gt;I got a pack of 3 on Indiegogo, hoping to receive them relatively soon. The wait turned out to be longer than I expected (shipping ended up being a few months late due to some complications in the production process) but I finally received my lights in October.&lt;/p&gt;
&lt;figure class=&quot;my-6&quot;&gt; &lt;img src=&quot;https://www.abahgat.com/_astro/box.DTskC9CD.jpg&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot; width=&quot;2637&quot; height=&quot;2867&quot; srcset=&quot;https://www.abahgat.com/_astro/box.DTskC9CD_Z1i1lHr.webp 400w, /_astro/box.DTskC9CD_1tYH6L.webp 768w, /_astro/box.DTskC9CD_16H58c.webp 1024w, /_astro/box.DTskC9CD_Z1PDJIk.webp 2040w, /_astro/box.DTskC9CD_114ak7.webp 2637w, /_astro/box.DTskC9CD_ZAIOA5.webp 5274w&quot; sizes=&quot;(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px&quot; style=&quot;object-fit: cover; object-position: center; max-width: 2637px; max-height: 2867px; aspect-ratio: 0.9197767701430066; width: 100%;&quot; alt=&quot;The box the lights come in.&quot; class=&quot;mx-auto rounded-md shadow-lg bg-gray-400 dark:bg-slate-700 w-full&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt; &lt;figcaption class=&quot;mt-2 text-center text-sm text-gray-500 dark:text-muted&quot;&gt; The box the lights come in. &lt;/figcaption&gt; &lt;/figure&gt;
&lt;p&gt;So far, I have been quite happy with them. I liked the fact that each light is configurable and offers a number of settings to customize that will help you make sure it works with your environment and preferences.&lt;/p&gt;
&lt;p&gt;I installed three lights (two are in bedrooms and a third is in the master bathroom) and set all of them to a warm, yellow glow. I initially configured the bathroom light to a multi-colored rotating pattern (see the screenshot below) but shortly after I opted for a more relaxing solid color and static pattern.&lt;/p&gt;
&lt;div id=&quot;gallery-3szhzgqpz&quot; class=&quot;gallery-wrapper not-prose&quot; data-images=&quot;[]&quot;&gt; &lt;div class=&quot;grid gap-2 my-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3&quot;&gt;  &lt;/div&gt; &lt;!-- Lightbox Modal --&gt; &lt;div class=&quot;lightbox-modal fixed inset-0 z-[9999] w-full h-full p-0 m-0 bg-transparent backdrop:bg-black/90 pointer-events-none hidden opacity-0 transition-opacity duration-300&quot; role=&quot;dialog&quot; aria-modal=&quot;true&quot;&gt; &lt;!-- Backdrop --&gt; &lt;div class=&quot;fixed inset-0 bg-black/90 backdrop-blur-sm pointer-events-auto modal-close-area&quot;&gt;&lt;/div&gt; &lt;div class=&quot;relative w-full h-full flex items-center justify-center p-4 pointer-events-none&quot;&gt; &lt;!-- Close Button --&gt; &lt;button class=&quot;absolute top-4 right-4 z-50 p-2 text-white/70 hover:text-white bg-black/50 rounded-full hover:bg-white/20 transition-all pointer-events-auto modal-close-btn&quot; aria-label=&quot;Close&quot;&gt; &lt;svg width=&quot;1em&quot; height=&quot;1em&quot; class=&quot;w-8 h-8&quot; data-icon=&quot;tabler:x&quot;&gt;   &lt;symbol id=&quot;ai:tabler:x&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M18 6L6 18M6 6l12 12&quot;/&gt;&lt;/symbol&gt;&lt;use href=&quot;#ai:tabler:x&quot;&gt;&lt;/use&gt;  &lt;/svg&gt; &lt;/button&gt; &lt;!-- Previous Button --&gt; &lt;button class=&quot;absolute left-4 z-50 p-3 text-white/70 hover:text-white bg-black/50 rounded-full hover:bg-white/20 transition-all pointer-events-auto prev-btn&quot; aria-label=&quot;Previous&quot;&gt; &lt;svg width=&quot;1em&quot; height=&quot;1em&quot; class=&quot;w-8 h-8&quot; data-icon=&quot;tabler:chevron-left&quot;&gt;   &lt;symbol id=&quot;ai:tabler:chevron-left&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;m15 6l-6 6l6 6&quot;/&gt;&lt;/symbol&gt;&lt;use href=&quot;#ai:tabler:chevron-left&quot;&gt;&lt;/use&gt;  &lt;/svg&gt; &lt;/button&gt; &lt;!-- Next Button --&gt; &lt;button class=&quot;absolute right-4 z-50 p-3 text-white/70 hover:text-white bg-black/50 rounded-full hover:bg-white/20 transition-all pointer-events-auto next-btn&quot; aria-label=&quot;Next&quot;&gt; &lt;svg width=&quot;1em&quot; height=&quot;1em&quot; class=&quot;w-8 h-8&quot; data-icon=&quot;tabler:chevron-right&quot;&gt;   &lt;symbol id=&quot;ai:tabler:chevron-right&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;m9 6l6 6l-6 6&quot;/&gt;&lt;/symbol&gt;&lt;use href=&quot;#ai:tabler:chevron-right&quot;&gt;&lt;/use&gt;  &lt;/svg&gt; &lt;/button&gt; &lt;!-- Main Image --&gt; &lt;img src=&quot;&quot; alt=&quot;Lightbox&quot; class=&quot;max-w-[90vw] max-h-[80vh] object-contain shadow-2xl rounded-sm pointer-events-auto lightbox-image transition-transform duration-300&quot;&gt; &lt;p class=&quot;lightbox-caption absolute bottom-4 left-0 right-0 text-center text-white bg-black/50 p-2 mx-auto max-w-3xl rounded backdrop-blur-sm pointer-events-auto opacity-0 transition-opacity duration-300&quot;&gt;&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; 
&lt;p&gt;The Zing app allows configuration of many parameters for the lights, such as the light color, intensitiy, spread (influencing how wide of an area the light would illuminate) and speed (for moving patterns).&lt;/p&gt;
&lt;p&gt;Unfortunately, the Android app seems to be lagging behind with respect to the iOS one in terms of functionality — more on this later.&lt;/p&gt;
&lt;div id=&quot;gallery-ldqdm5vvw&quot; class=&quot;gallery-wrapper not-prose&quot; data-images=&quot;[]&quot;&gt; &lt;div class=&quot;grid gap-2 my-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3&quot;&gt;  &lt;/div&gt; &lt;!-- Lightbox Modal --&gt; &lt;div class=&quot;lightbox-modal fixed inset-0 z-[9999] w-full h-full p-0 m-0 bg-transparent backdrop:bg-black/90 pointer-events-none hidden opacity-0 transition-opacity duration-300&quot; role=&quot;dialog&quot; aria-modal=&quot;true&quot;&gt; &lt;!-- Backdrop --&gt; &lt;div class=&quot;fixed inset-0 bg-black/90 backdrop-blur-sm pointer-events-auto modal-close-area&quot;&gt;&lt;/div&gt; &lt;div class=&quot;relative w-full h-full flex items-center justify-center p-4 pointer-events-none&quot;&gt; &lt;!-- Close Button --&gt; &lt;button class=&quot;absolute top-4 right-4 z-50 p-2 text-white/70 hover:text-white bg-black/50 rounded-full hover:bg-white/20 transition-all pointer-events-auto modal-close-btn&quot; aria-label=&quot;Close&quot;&gt; &lt;svg width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; class=&quot;w-8 h-8&quot; data-icon=&quot;tabler:x&quot;&gt;   &lt;use href=&quot;#ai:tabler:x&quot;&gt;&lt;/use&gt;  &lt;/svg&gt; &lt;/button&gt; &lt;!-- Previous Button --&gt; &lt;button class=&quot;absolute left-4 z-50 p-3 text-white/70 hover:text-white bg-black/50 rounded-full hover:bg-white/20 transition-all pointer-events-auto prev-btn&quot; aria-label=&quot;Previous&quot;&gt; &lt;svg width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; class=&quot;w-8 h-8&quot; data-icon=&quot;tabler:chevron-left&quot;&gt;   &lt;use href=&quot;#ai:tabler:chevron-left&quot;&gt;&lt;/use&gt;  &lt;/svg&gt; &lt;/button&gt; &lt;!-- Next Button --&gt; &lt;button class=&quot;absolute right-4 z-50 p-3 text-white/70 hover:text-white bg-black/50 rounded-full hover:bg-white/20 transition-all pointer-events-auto next-btn&quot; aria-label=&quot;Next&quot;&gt; &lt;svg width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; class=&quot;w-8 h-8&quot; data-icon=&quot;tabler:chevron-right&quot;&gt;   &lt;use href=&quot;#ai:tabler:chevron-right&quot;&gt;&lt;/use&gt;  &lt;/svg&gt; &lt;/button&gt; &lt;!-- Main Image --&gt; &lt;img src=&quot;&quot; alt=&quot;Lightbox&quot; class=&quot;max-w-[90vw] max-h-[80vh] object-contain shadow-2xl rounded-sm pointer-events-auto lightbox-image transition-transform duration-300&quot;&gt; &lt;p class=&quot;lightbox-caption absolute bottom-4 left-0 right-0 text-center text-white bg-black/50 p-2 mx-auto max-w-3xl rounded backdrop-blur-sm pointer-events-auto opacity-0 transition-opacity duration-300&quot;&gt;&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; 
&lt;p&gt;The predictive path lighting feature has been quite disappointing so far. Whenever one of the lights turns on (because you are walking past it), all the other two will turn on as well, almost as if the model powering the feature today wasn’t any sophisticated than “if motion is detected, turn on &lt;em&gt;all&lt;/em&gt; lights”. Not a big deal, but it meant that I turned off the feature on the light in the other bedroom, since I did not want any of getting up to trigger the light in our daughter’s room.&lt;/p&gt;
&lt;p&gt;I haven’t gotten to try neither the locator or the notification feature advertised on Indiegogo: I am not sure whether they are supported or not.&lt;/p&gt;
&lt;p&gt;Unfortunately, the version of the lights I received shipped with an older firmware version that is affected by a couple issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;some settings (e.g. blue light reduction) are not persisted if the light loses power;&lt;/li&gt;
&lt;li&gt;the activity indicator for the WiFi module often flashes blue.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I must say the latter issue is quite annoying in a night light. If you think about it, having a bright blue LED flash unexpectedly is quite noticeable in a dark room and almost defeats the purpose of having a night light.&lt;/p&gt;
&lt;p&gt;I am told that updating the device firmware might help with both of these issues but unfortunately the Android application is unable to perform the update so far. I have been in touch with the Zing Support team to understand what workaround are available (other than procuring an iPhone) and I am hoping to hear back soon.&lt;/p&gt;
&lt;p&gt;All considered, I have been quite happy with Zing, provided that I manage to fix the issue with the WiFi module.&lt;/p&gt;
&lt;p&gt;The features that I wish it had at this point are all related to software and am hoping they might happen soon:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Being able to prevent the lights from turning on at daytime or when the room is already bright enough;&lt;/li&gt;
&lt;li&gt;IFTTT/Google Assistant integration;&lt;/li&gt;
&lt;li&gt;Being able to access the temperature sensor via API.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are curious to check Zing out, they now have &lt;a href=&quot;https://zing.fm/&quot;&gt;an official site&lt;/a&gt;.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;&lt;strong&gt;UPDATE (January 2021)&lt;/strong&gt; I am still quite happy with the basic functionality of these lights. However, I can’t recommend them if you are an Android user.&lt;/p&gt;
&lt;p&gt;The Android application to control the lights has not received any updates in years. It also lacks several features that are present on the iOS version, such as controlling the lights based on a schedule, upgrading the device firmware and more.&lt;/p&gt;</content:encoded></item><item><title>Migrating From Wordpress to Hugo</title><link>https://www.abahgat.com/blog/migrating-from-wordpress-to-hugo</link><guid isPermaLink="true">https://www.abahgat.com/blog/migrating-from-wordpress-to-hugo</guid><description>After many years of running my site on Wordpress, I just migrated this site to Hugo. The migration was quite simple, this post outlines the main steps and offers a few helpful resources.</description><pubDate>Thu, 15 Mar 2018 00:35:57 GMT</pubDate><content:encoded>&lt;p&gt;After many years of running my own site on Wordpress, I finally pulled the trigger and decided to migrate to a different stack.&lt;/p&gt;
&lt;p&gt;Wordpress had been working quite well for me until I started to run into some with the hosted version and did not want to deal with having to set up and maintain my own server just for this site.&lt;/p&gt;
&lt;p&gt;When I found myself, unexpectedly, with some time to spare — rocking my newborn daughter back to sleep in the middle of the night — I took it as an opportunity to learn what kind of options are available for running simple websites in 2018. I had read so much about &lt;a href=&quot;https://www.smashingmagazine.com/2015/11/modern-static-website-generators-next-big-thing/&quot;&gt;static site generators&lt;/a&gt; and they seemed such a great fit for what I was trying to do, so I decided to give it a shot.&lt;/p&gt;
&lt;p&gt;I am surprised to see how far things have made it since when I last looked. If are interested in the current state of things, you can find a pretty good list on &lt;a href=&quot;http://staticgen.com&quot;&gt;StaticGen.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I had no shortage of alternatives to consider but I fairly quickly settled on setting my new site up with &lt;a href=&quot;https://gohugo.io&quot;&gt;Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thankfully, the migration itself was not too daunting, I was able to complete most of it during the course of a few nights while holding a sleeping baby 😉&lt;/p&gt;
&lt;p&gt;In case you are considering doing the same migration, here an outline of the steps involved and a few articles I would recommend.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Decide whether you want to keep the same apperance or you are okay with selecting a theme you like and just exporting your comment. In my case, I decided to switch to a &lt;a href=&quot;https://themes.gohugo.io/academic/&quot;&gt;new theme&lt;/a&gt;, so I focused on mapping how my existing content would be organized in the theme I was migrating to.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Migrate your content to Markdown that Hugo can process. I found this article useful: &lt;a href=&quot;https://sourcethemes.com/academic/docs/migrate-from-wordpress/&quot;&gt;Migrating from Wordpress&lt;/a&gt;. Requires installing a plugin on your Wordpress site to export content in a format that &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; (another static site generator) can process and then transform that to the format Hugo expects&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;alert alert-warning&quot;&gt;If your site is on wordpress.com, the guide above won&apos;t work as is, since you will not be able to install plugins unless you are hosting your own server. I worked my way around this by [exporting an XML dump of my site](https://en.support.wordpress.com/export/), and then starting up a throwaway wordpress server (I did this with cloud9 when they offered a free plan, you can probably get a similar result by running it on [docker](https://docs.docker.com/compose/wordpress/)).&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Your site will likely require some fixes at this point. The specifics depend on what it looks like but it is likely that you will want at least to verify that the links between pages are working fine. Images often require some fixes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My site had a fair number of incoming links from other places. I wanted to avoid breaking them if possible. This is where I was glad I was deploying my site on &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;, since they offer great support of &lt;a href=&quot;https://www.netlify.com/docs/redirects/&quot;&gt;Redirect &amp;#x26; Rewrite Rules&lt;/a&gt;, among many other features.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I had a good number of comments on my old site and I wanted to carry them over. For the sake of simplicity, I chose to use Disqus for my comments and thankfully they had a good article about &lt;a href=&quot;https://help.disqus.com/en/articles/1717131-importing-comments-from-wordpress&quot;&gt;Importing comments from WordPress&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;alert alert-note&quot;&gt;Disqus comments are associated with page URLs, so you will want to make sure your pages are served at the same URLs as before the migration. Alternatively, you can edit the URLs in the export file before importing it following the instructions above.&lt;/div&gt;
&lt;p&gt;I have yet to find a technical migration that completes without introducing new issues, so if you ever encounter any bugs on this site, I would ask you to &lt;a href=&quot;https://www.abahgat.com/#contact&quot;&gt;please let me know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Feel free to leave a comment if you are trying to do the same migration and you run into trouble, I can try to help you out.&lt;/p&gt;</content:encoded></item><item><title>What’s wrong with Milan’s Open Data initiative</title><link>https://www.abahgat.com/blog/whats-wrong-with-milans-open-data-initiative</link><guid isPermaLink="true">https://www.abahgat.com/blog/whats-wrong-with-milans-open-data-initiative</guid><description>I spent some time playing with the Open Data published by the City of Milan, aiming to visualize public transport coverage. While I managed to create a heatmap, I was left unsatisfied by the data presentation and format. The initiative is promising but could be dramatically improved by adopting modern standards like GeoJSON instead of Shapefiles and including simple preview capabilities.</description><pubDate>Thu, 12 Sep 2013 10:38:51 GMT</pubDate></item><item><title>Appsterdam Guru Session: Google App Engine for beginners</title><link>https://www.abahgat.com/blog/appsterdam-guru-session-google-app-engine-for-beginners</link><guid isPermaLink="true">https://www.abahgat.com/blog/appsterdam-guru-session-google-app-engine-for-beginners</guid><description>One of the things I was not expecting when I moved to Amsterdam was its active and vibrant tech community. Appsterdam, a non-profit organization focused around aggregating people with a passion for te...</description><pubDate>Sat, 06 Jul 2013 02:21:19 GMT</pubDate><content:encoded>&lt;p&gt;One of the things I was not expecting when I &lt;a href=&quot;https://www.abahgat.com/post/2012-11-20-what-you-should-know-before-moving-to-amsterdam/index.md&quot; title=&quot;What you should know before moving to Amsterdam&quot;&gt;moved to Amsterdam&lt;/a&gt; was its active and vibrant tech community. &lt;a href=&quot;http://appsterdam.rs/&quot;&gt;Appsterdam&lt;/a&gt;, a non-profit organization focused around aggregating people with a passion for technology, is probably one of the central forces in this movement.&lt;/p&gt;
&lt;p&gt;In my year in Amsterdam I had been to a few meetups organized by people from Appsterdam and always came back home having learned something new. This is why when my colleague &lt;a href=&quot;https://twitter.com/mattfgl&quot;&gt;Matt&lt;/a&gt; (who himself is quite an active Appsterdam member) talked me into presenting a guru session on Google App Engine, I saw that as an opportunity to return the favor.&lt;/p&gt;
&lt;p&gt;While I tried to give an overview of App Engine in general (and the Python flavor, specifically), I also wanted to offer attendees the chance to work on some examples that were more interesting than the typical guestbook application that comes with all the tutorials you can find online.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/abahgat/gae4beginners-demos&quot;&gt;code examples&lt;/a&gt; build on two of the many APIs App Engine has to offer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;a href=&quot;https://developers.google.com/appengine/docs/python/channel/&quot;&gt;Channel API&lt;/a&gt; to build a web page that displays the current cursor position of every user looking at that site,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/appengine/docs/python/endpoints/&quot;&gt;Google Cloud Endpoints&lt;/a&gt; to implement a simple REST-like backend for a webpage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can check out the &lt;a href=&quot;https://speakerdeck.com/abahgat/google-app-engine-for-beginners&quot;&gt;slide deck&lt;/a&gt; below and get the code examples from the &lt;a href=&quot;https://github.com/abahgat/gae4beginners-demos&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;[Embedded content not available in RSS — &lt;a href=&quot;https://www.abahgat.com/blog/appsterdam-guru-session-google-app-engine-for-beginners&quot;&gt;view on site&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;App Engine is a lot more than an advanced infrastructure to deploy applications: the numerous APIs and services it offers can enable developers to build advanced applications with limited effort. I hope this presentation, while just scratching the surface, gives you a glimpse on the possibilities.&lt;/p&gt;
&lt;hr&gt;
&lt;p style=&quot;font-size:smaller;&quot;&gt;
  Special thanks to &lt;a href=&quot;https://twitter.com/mattfgl&quot;&gt;Matt&lt;/a&gt; for pushing me to do this and &lt;a href=&quot;https://twitter.com/chiya_serena&quot;&gt;Serena&lt;/a&gt; for her help with example 2.
&lt;/p&gt;</content:encoded></item><item><title>Presenting Professional Invaders</title><link>https://www.abahgat.com/blog/presenting-professional-invaders</link><guid isPermaLink="true">https://www.abahgat.com/blog/presenting-professional-invaders</guid><description>The story of Professional Invaders, the game we built during TNW Conference&apos;s Hack Battle.</description><pubDate>Thu, 06 Jun 2013 07:25:00 GMT</pubDate><content:encoded>&lt;p&gt;A few weeks ago I attended &lt;a href=&quot;http://thenextweb.com/conference/&quot; target=&quot;_blank&quot;&gt;The Next Web Conference&lt;/a&gt; in Amsterdam and joined a bunch of fellow programmers for another edition of the Kings of Code Hack Battle, the same kind of event as the one where &lt;a title=&quot;Story of a hack: Bring Your Own Music!&quot; href=&quot;https://www.abahgat.com/blog/story-of-a-hack-bring-your-own-music/&quot; target=&quot;_blank&quot;&gt;Bring Your Own Music&lt;/a&gt; was born.&lt;/p&gt;
&lt;p&gt;Following the usual schedule, after a brief presentation from the API partners (Spotify, SendGrid, Braintree, Deezer, Pearson, Nokia, Rebtel, Bol.com, Smart TV Alliance and LinkedIn), all the attendees started evaluating ideas about what to build.&lt;/p&gt;
&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-permalink=&quot;https://www.instagram.com/p/Yf2NZsjz_5/?utm_source=ig_embed&amp;#x26;utm_campaign=loading&quot; data-instgrm-version=&quot;13&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/Yf2NZsjz_5/?utm_source=ig_embed&amp;#x26;utm_campaign=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/p/Yf2NZsjz_5/?utm_source=ig_embed&amp;#x26;utm_campaign=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;&quot; target=&quot;_blank&quot;&gt;A post shared by Alessandro Bahgat (@abahgat)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt; 
&lt;p&gt;I teamed up with &lt;a href=&quot;http://alexdeleon.name/&quot;&gt;Alexander&lt;/a&gt;, a friend of mine I already had the chance to work with back in the days when I when I was consulting.&lt;/p&gt;
&lt;p&gt;Having LinkedIn among the sponsors seemed to encourage us to build serious applications for serious professionals, but after discarding a few alternatives that would have been better projects for a Startup Weekend than a hackathon, we decided to take the opposite direction: building the silliest possible thing with the APIs we had access to.&lt;/p&gt;
&lt;p&gt;We eventually decided to work on a game and tried to build a Space Invaders clone that would let you throw paper balls at your professional connections.&lt;/p&gt;
&lt;p&gt;After some research, we found a well written Space Invaders implementation on GitHub (thanks &lt;a href=&quot;https://github.com/Calamari&quot;&gt;Calamari&lt;/a&gt;) and we started adding the silliness to it.&lt;/p&gt;
&lt;p&gt;The first day we focused on getting the game to work as we expected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;each invader would be one of your connections on LinkedIn,&lt;/li&gt;
&lt;li&gt;a Boss would spawn every now and then,&lt;/li&gt;
&lt;li&gt;the game would have some sort of soundtrack (thanks Deezer),&lt;/li&gt;
&lt;li&gt;while in “Boss mode”, the game would have a distinctive appearance (blinking red background and a different theme song).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The second day we turned our attention to features that were just fun to build:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a coin slot where players could buy more coins with their own credit card (API courtesy of Braintree),&lt;/li&gt;
&lt;li&gt;an easter egg we planned to use in the demo: attendees could spawn the Boss by sending email to an address we set up for the occasion (thanks Sendgrid).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The video below (3:11) shows the major changes the application went through. It was created by replaying significant entries in the commit log and recording what the game looked like at that time.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;[Embedded content not available in RSS — &lt;a href=&quot;https://www.abahgat.com/blog/presenting-professional-invaders&quot;&gt;view on site&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We approached the deadline with only one objective: making people laugh. Despite some technical issues (amusing at a tech conference), we managed to demo our hack and people seemed to have liked it: the guys from Sendgrid even decided to award us with a prize 🙂&lt;/p&gt;
&lt;p&gt;You play the game &lt;a title=&quot;Play Professional Invaders&quot; href=&quot;http://alexdeleon.github.io/professional_invaders/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;. This version is a slightly different from what we presented at the hack battle, since we decided to keep only the features that made sense if we were to offer it online.&lt;/p&gt;
&lt;p&gt;We hope you’ll have as much fun playing it as we had putting it together!&lt;/p&gt;</content:encoded></item><item><title>What Van Gogh can teach us about persistence</title><link>https://www.abahgat.com/blog/what-van-gogh-can-teach-us-about-persistence</link><guid isPermaLink="true">https://www.abahgat.com/blog/what-van-gogh-can-teach-us-about-persistence</guid><description>I visited the Van Gogh museum in Amsterdam recently and, to my surprise, I left the exposition having learned something that matters beyond art.</description><pubDate>Mon, 04 Mar 2013 13:33:43 GMT</pubDate><content:encoded>&lt;p&gt;I visited the Van Gogh museum in Amsterdam recently and, to my surprise, I left the exposition having learned something that matters beyond art.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;According to his &lt;a href=&quot;http://en.wikipedia.org/wiki/Van_Gogh&quot;&gt;biography&lt;/a&gt;,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Van Gogh began to draw as a child, and he continued to draw throughout the years that led up to his decision to become an artist. He did not begin painting until his late twenties, completing many of his best-known works during the last two years of his life. In just over a decade, he produced more than 2,100 artworks, consisting of &lt;a href=&quot;http://en.wikipedia.org/wiki/List_of_works_by_Vincent_van_Gogh&quot; title=&quot;List of works by Vincent van Gogh&quot;&gt;860 oil paintings&lt;/a&gt; and more than &lt;a href=&quot;http://en.wikipedia.org/wiki/Drawings,_water-colours_and_prints_by_Vincent_van_Gogh&quot; title=&quot;Drawings, water-colours and prints by Vincent van Gogh&quot;&gt;1,300 watercolors, drawings, sketches and prints&lt;/a&gt;. […]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before focusing on painting, he worked as an art dealer, teacher and missionary. It wasn’t until he was 32 that he painted his first major work.&lt;/p&gt;
&lt;p&gt;He did not have the fortune of being recognized as a talented artist in his young age like Michelangelo and others and yet still he did not let go of his desire of becoming a painter. The thing that strikes most of the museum is the quantity of studies and sketches Van Gogh made throughout his live in order to improve his skills. &lt;span style=&quot;font-style:inherit;line-height:1.625;&quot;&gt;He wanted to paint so much that &lt;/span&gt;&lt;strong style=&quot;font-style:inherit;line-height:1.625;&quot;&gt;he kept practicing&lt;/strong&gt;&lt;span style=&quot;font-style:inherit;line-height:1.625;&quot;&gt; and put so much effort in improving that it eventually paid off: he is now remembered as the author of &lt;/span&gt;&lt;span style=&quot;font-style:inherit;line-height:1.625;&quot;&gt;dozens of the most renown paintings of the history of art.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In an age where the reference point to define an accomplishment is starting a company at 16 and become a billionaire at 22, we risk underestimating the value of persistence. Sure, he did not reach fame and success while he was alive, and his life was not what you would define “happy”. But if he had quit because he was not an accomplished painter in his young age, art now would certainly be very different from what we know.&lt;/p&gt;
&lt;p&gt;The works of V&lt;span style=&quot;font-style:inherit;line-height:1.625;&quot;&gt;an Gogh are a proof that there is no such thing as &lt;/span&gt;&lt;strong style=&quot;font-style:inherit;line-height:1.625;&quot;&gt;being too late to accomplish something remarkable&lt;/strong&gt;&lt;span style=&quot;font-style:inherit;line-height:1.625;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title>Prettier source code on WordPress.com</title><link>https://www.abahgat.com/blog/prettier-source-code-on-wordpress-com</link><guid isPermaLink="true">https://www.abahgat.com/blog/prettier-source-code-on-wordpress-com</guid><description>Posting source code on WordPress.com is quite simple: the platform already provides an extremely easy to use shortcode called sourcecode, based on a fairly flexible syntax highlighter plugin. By looki...</description><pubDate>Mon, 21 Jan 2013 10:34:50 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.abahgat.com/img/wp-uploads/2013/01/screen-shot-2013-01-16-at-10-44-59-pm.png&quot; alt=&quot;Formatted source code&quot; loading=&quot;lazy&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Posting source code on WordPress.com is quite simple: the platform already provides an extremely easy to use shortcode called &lt;a href=&quot;http://en.support.wordpress.com/code/posting-source-code/&quot;&gt;&lt;code&gt;sourcecode&lt;/code&gt;&lt;/a&gt;, based on a fairly flexible syntax highlighter plugin. By looking at the examples in the &lt;a href=&quot;http://en.support.wordpress.com/code/posting-source-code/&quot;&gt;documentation page&lt;/a&gt;, however, it is evident that the default styling used to render sources is quite old-fashioned and does not fit most modern themes.&lt;/p&gt;
&lt;p&gt;While the shortcode offers options to allow users to control many options of the rendering, it does not allow us to configure colors, fonts and size (the default size is so tiny that it is barely readable on high-resolution screens).&lt;/p&gt;
&lt;p&gt;When I was writing the previous technical post, I did some investigations to figure out what options are available to post more readable sources if your blog is hosted on WordPress.com and I found out there are basically two alternatives.&lt;/p&gt;
&lt;h1 id=&quot;embedding-gists&quot;&gt;Embedding Gists&lt;/h1&gt;
&lt;p&gt;The easiest option is to rely on &lt;a href=&quot;https://gist.github.com/&quot;&gt;Gist&lt;/a&gt; – GitHub’s tool for sharing snippets of code – which offers an extremely easy way to embed code in your blog. Just create a new snippet (gist) there and &lt;a href=&quot;http://en.support.wordpress.com/gist/&quot;&gt;follow the instructions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, the gist embed shortcode available on WordPress.com is less flexible than what you would get if you installed it as a &lt;a href=&quot;http://wordpress.org/extend/plugins/embed-github-gist/&quot;&gt;plugin&lt;/a&gt; on your own instance of WordPress, but it will be enough for most cases.&lt;/p&gt;
&lt;table&gt;&lt;tr&gt;&lt;th&gt;&lt;p&gt;Pros&lt;/p&gt;&lt;/th&gt;&lt;th&gt;&lt;p&gt;Cons&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Easy to embed source&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Suitable for posts with a few (long) code snippets&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Code looks good and is readable&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Does not always work perfectly with search engines&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Easy for readers to access raw code&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Does not work with RSS and posts over email&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h1 id=&quot;styling-source-code-by-customizing-your-css&quot;&gt;Styling source code by customizing your CSS&lt;/h1&gt;
&lt;p&gt;While Gists work great most of the time, they are a pain to create and maintain if you are working on a post that should include multiple short snippets of code. In that case, the amount of bookkeeping you have to do is significant (you will have to create and link many small chunks of code) and you may want to be able to manage your code right within the post.&lt;/p&gt;
&lt;p&gt;In that case, it may be more practical to fix the CSS theme used by the syntax highlight plugin to make it look post-2010. If you set your own custom CSS on WordPress.com, it will be supposed to be included as the last one to allow you to redefine the styles specified by the theme you are using.&lt;/p&gt;
&lt;p&gt;Unfortunately, the CSS used by the syntax highlight module was clearly not written with extensibility in mind, but quite the opposite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;all the style declarations it includes make use of &lt;code&gt;!important&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;the plugin will dynamically include its own CSS as the last item in the &lt;code&gt;head&lt;/code&gt; node, meaning that it will have preference on the custom one you define.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes sense in the original context – the original syntax highlighter offered several themes you could choose from by including different stylesheets, but that feature is not available on WordPress.com – but will make your life more difficult. You will need to add &lt;code&gt;!important&lt;/code&gt; to &lt;strong&gt;all the CSS declarations you redefine&lt;/strong&gt; and you will need to use CSS selectors that are &lt;strong&gt;more specific&lt;/strong&gt; than the ones used by the plugin. You will be able to see the final result at the end of this post.&lt;/p&gt;
&lt;p&gt;WordPress’s syntax highlight is not perfect, and some things are still quite annoying (e.g. line numbers get in the way if you try selecting and copying source code). Most issues could be addressed by upgrading the plugin to use version 3 of &lt;a href=&quot;http://alexgorbatchev.com/SyntaxHighlighter/&quot;&gt;SyntaxHighlighter&lt;/a&gt; instead of the outdated version that is in use now, but it is something you will not be able to control unless the folks at Automattic decide to update it.&lt;/p&gt;
&lt;table&gt;&lt;tr&gt;&lt;th&gt;&lt;p&gt;Pros&lt;/p&gt;&lt;/th&gt;&lt;th&gt;&lt;p&gt;Cons&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;It is necessary to have access to Custom CSS (which is a paid feature)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Hard to copy sources without including line numbers (unless you disable them)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Access to advanced features (highlight lines, toggle line number display)&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Search engines index sources with the post content&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Source can be styled according to preference&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Getting your CSS applied correctly can be difficult (but you can start from &lt;a href=&quot;https://gist.github.com/4464280&quot;&gt;here&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h1 id=&quot;what-did-i-choose&quot;&gt;What did I choose?&lt;/h1&gt;
&lt;p&gt;Here is the &lt;a href=&quot;https://gist.github.com/4464280&quot;&gt;stylesheet&lt;/a&gt; (embedded as a Gist) I am currently using  on this blog, based on the pygments theme used to style code at &lt;a href=&quot;http://docs.python.org/&quot; title=&quot;docs.python.org&quot;&gt;docs.python.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;[Embedded content not available in RSS — &lt;a href=&quot;https://www.abahgat.com/blog/prettier-source-code-on-wordpress-com&quot;&gt;view on site&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt; 
&lt;p&gt;You can can see what the final result looks like &lt;del&gt;in this post about &lt;a href=&quot;https://www.abahgat.com/post/2013-01-07-user-authentication-with-webapp2-on-google-app-engine/index.md&quot; title=&quot;User authentication with webapp2 on Google App Engine&quot;&gt;User authentication with webapp2 on Google App Engine&lt;/a&gt;&lt;/del&gt; and in the image at the beginning of this post.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update: I since migrated this blog to a new system, and am using a completely different way to render source code.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>User authentication with webapp2 on Google App Engine</title><link>https://www.abahgat.com/blog/user-authentication-with-webapp2-on-google-app-engine</link><guid isPermaLink="true">https://www.abahgat.com/blog/user-authentication-with-webapp2-on-google-app-engine</guid><description>Google App Engine for Python ships with the capability to manage user accounts without the need of any additional library. This functionality is, however, insufficiently documented. This post is a step-by-step tutorial addressing user registration, login, password reset and a few other details.</description><pubDate>Mon, 07 Jan 2013 07:57:26 GMT</pubDate></item><item><title>Beyond keyboard shortcuts</title><link>https://www.abahgat.com/blog/beyond-keyboard-shortcuts</link><guid isPermaLink="true">https://www.abahgat.com/blog/beyond-keyboard-shortcuts</guid><description>In the age of touch devices, some days it seems like a day will come when we will not have to use a keyboard to interact with computers. A significant part of our relationship with technology passes t...</description><pubDate>Wed, 19 Dec 2012 08:13:57 GMT</pubDate></item><item><title>Story of a hack: Bring Your Own Music!</title><link>https://www.abahgat.com/blog/story-of-a-hack-bring-your-own-music</link><guid isPermaLink="true">https://www.abahgat.com/blog/story-of-a-hack-bring-your-own-music</guid><description>This post is a summary of the weekend we spent at the Kings of Code 2012 Hack Battle in Amsterdam. What started as an occasion to get to know smart people doing cool things in Amsterdam (something I l...</description><pubDate>Mon, 03 Dec 2012 23:19:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is a summary of the weekend we spent at the &lt;a href=&quot;http://kingsofcode.com/&quot;&gt;Kings of Code 2012&lt;/a&gt; Hack Battle in Amsterdam. What started as an occasion to get to know smart people doing cool things in Amsterdam (something I look for since &lt;a href=&quot;https://www.abahgat.com/post/2012-11-20-what-you-should-know-before-moving-to-amsterdam/index.md&quot; title=&quot;What you should know before moving to Amsterdam&quot;&gt;I moved here&lt;/a&gt;) turned out to be one of the funniest experiences I had in a while.&lt;/p&gt;
&lt;p&gt;After a brief presentation of the services offered by the hackathon partners (&lt;a href=&quot;http://apigee.com/&quot;&gt;Apigee&lt;/a&gt;, &lt;a href=&quot;http://www.esri.com/&quot;&gt;Esri&lt;/a&gt;, &lt;a href=&quot;http://www.spotify.com/&quot;&gt;Spotify&lt;/a&gt; and &lt;a href=&quot;http://sendgrid.com/&quot;&gt;Sendgrid&lt;/a&gt;) &lt;a href=&quot;http://about.me/diderik/&quot;&gt;Diderik&lt;/a&gt;, &lt;a href=&quot;http://www.raibaz.com/&quot;&gt;Mattia&lt;/a&gt;, &lt;a href=&quot;https://github.com/mikepage&quot;&gt;Mike&lt;/a&gt; and I teamed up to build the hack featured here. We started with the most obvious concept we could come up with: putting songs on a map and having people visualize them. We tried to elaborate the concept to include as many of the partners’ APIs as we could, but then we decided for something simpler, something we could build over the weekend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.abahgat.com/img/wp-uploads/2012/12/2012-12-02-13-01-47.jpg&quot; alt=&quot;Bring Your Own Music card reader&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;It took us a couple of iterations to get to the final idea we developed: &lt;strong&gt;Bring Your Own Music&lt;/strong&gt;, a toy application that allows users to control music playback through NFC-enabled objects by using an Arduino-powered NFC reader driving a Spotify app.&lt;/p&gt;
&lt;p&gt;BYOM is based upon the idea of attaching Spotify playlists to physical objects, provided they contain an NFC tag, like many of the cards we have in our wallet. Once you have done that, you can then bring your card with you to a party, tap it on an NFC reader, and have a Jukebox Spotify app like the one we built start playing your songs automatically.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.abahgat.com/img/wp-uploads/2012/12/byomscreenshot.png&quot; alt=&quot;A screenshot of the Spotify Jukebox app&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;If multiple people tap their own cards, the main Spotify playlist adjusts itself according to the general preferences of the party attendees. We even kept playing with NFC-enabled objects and linked Spotify playlists to all sort of cards, like London’s Oyster cards, OV-Chipkaarts and even the disposable tram tickets you get here in Amsterdam.&lt;/p&gt;
&lt;p&gt;As Mattia noted,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;wouldn’t it be great if passengers could listen to their favorite music when they check-in on trams?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;how-was-byom-built&quot;&gt;How was BYOM built?&lt;/h2&gt;
&lt;p&gt;We collected the identifiers of a bunch of NFC cards, OV-chipkaarts and tram tickets using Mike’s Arduino connected NFC reader. We then created a collection on &lt;a href=&quot;http://apigee.com/docs/app_services&quot;&gt;Apigee App Services&lt;/a&gt; to store the card ids and URIs of the Spotify playlists we wanted to link to them.&lt;/p&gt;
&lt;p&gt;We programmed Arduino to make a call to Apigee whenever a card is tapped on the reader, in order to store the card identifier into a &lt;em&gt;Swipes&lt;/em&gt; collection.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.abahgat.com/img/wp-uploads/2012/12/2012-12-02-17-33-16.jpg&quot; alt=&quot;Hey, look, there is an Arduino in the box!&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;At the same time, we wrote a Spotify application that periodically queried Apigee for new swipes and, as soon as a new card was detected, fetched the songs associated to it and merged them into the main playlist (the one the Spotify Jukebox app would play).&lt;/p&gt;
&lt;h2 id=&quot;what-have-we-learned-by-it&quot;&gt;What have we learned by it?&lt;/h2&gt;
&lt;p&gt;We (I, at least) walked away with some important teachings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;focus&lt;/strong&gt; is more important than ever: there is no point in imagining lots of features if you are not going to finish them. Pick a small set of features and make them work (or almost work: we had the inevitable demo-time malfunctions despite having tried the full example multiple times before presenting),&lt;/li&gt;
&lt;li&gt;build a &lt;strong&gt;story&lt;/strong&gt;: having a story (in our case, the party) around your hack helps you focus and helps you when presenting it,&lt;/li&gt;
&lt;li&gt;get some &lt;strong&gt;constraints&lt;/strong&gt;: there are thousands of interesting APIs, devices and ideas out there, and even more ways those can be combined. Choose some constraints to ensure you don’t wander off too much (in our case, we wanted to do something with Mike’s NFC-reader)&lt;/li&gt;
&lt;li&gt;have &lt;strong&gt;fun&lt;/strong&gt;: while these sounds like rules, they are more of an advice. Do not let them distract you from your goal: &lt;em&gt;having fun&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-about-the-technologies&quot;&gt;What about the technologies?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Arduino&lt;/strong&gt; is indeed an extremely versatile tool and the amount of sample code and libraries available out there is surprising. While it can be a pain to debug at times, the fact that it allows you to add a physical dimension to your hacks is invaluable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spotify&lt;/strong&gt;‘s wise choice to build their application framework on top of Chromium means that developers can leverage the tools they already got to know by developing web applications. The actual app SDK is a JavaScript library you an include and debug using the tools in Chromium. We were able to get started in minutes and, once &lt;a href=&quot;https://twitter.com/mager&quot;&gt;Andrew&lt;/a&gt; put us on the right track (by pointing us at the experimental but more powerful 1.0 API), never hit a wall or get stuck at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apigee App Services&lt;/strong&gt; is indeed a valuable tool: it saved us a lot of time by taking care of storing our data and, more important, exposing it over the internet with no hassle. (The fact that we were even able to interact with it through an Arduino is not trivial.) It also took away the need of building an admin interface for most basic needs. Quoting Diderik&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apigee allowed us to get so much done on the first day of the hackathon that we could go home and get some sleep for the night.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;how-did-it-go&quot;&gt;How did it go?&lt;/h2&gt;
&lt;p&gt;Not bad at all. We managed to win a couple of prizes: each of us won a 6 months premium subscription to Spotify (thanks &lt;a href=&quot;https://twitter.com/mager&quot;&gt;Andrew&lt;/a&gt;!) and a gift certificate courtesy of Apigee (thanks &lt;a href=&quot;https://twitter.com/timanglade&quot;&gt;Tim&lt;/a&gt;!). All in all, we did not build BYOM for the prizes, but we were glad to get them.&lt;/p&gt;
&lt;p&gt;The most important thing is that we had great fun coming out with a solution that worked and thinking about how to communicate what seemed so cool to us. We had even more fun seeing what the other teams had built (there were some really brilliant hacks indeed). Isn’t that what a hackathon should be about?&lt;/p&gt;
&lt;h2 id=&quot;can-i-see-it&quot;&gt;Can I see it?&lt;/h2&gt;
&lt;p&gt;Sure, you can check the &lt;a href=&quot;https://github.com/abahgat/byom&quot;&gt;source code&lt;/a&gt;. Beware: it’s hackathon-grade code, so it probably does not look like the clean code you would normally want to write during the day. If you are curious, &lt;a href=&quot;http://www.ustream.tv/recorded/27445421&quot;&gt;here is a video&lt;/a&gt; with Bring Your Own Music and all the other cool hacks built at Kings of Code 2012 Hack Battle. (It is a bit dark, but you get the idea.)&lt;/p&gt;
&lt;p&gt;More from us:
&lt;a href=&quot;https://twitter.com/diderikvw?ref_src=twsrc%5Etfw&quot; class=&quot;twitter-follow-button&quot; data-show-count=&quot;false&quot;&gt;Follow @diderikvw&lt;/a&gt;
&lt;a href=&quot;https://twitter.com/nldmike?ref_src=twsrc%5Etfw&quot; class=&quot;twitter-follow-button&quot; data-show-count=&quot;false&quot;&gt;Follow @nldmike&lt;/a&gt;
&lt;a href=&quot;https://twitter.com/raibaz?ref_src=twsrc%5Etfw&quot; class=&quot;twitter-follow-button&quot; data-show-count=&quot;false&quot;&gt;Follow @raibaz&lt;/a&gt;
&lt;a href=&quot;https://twitter.com/abahgat?ref_src=twsrc%5Etfw&quot; class=&quot;twitter-follow-button&quot; data-show-count=&quot;false&quot;&gt;Follow @abahgat&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you guys for the great weekend!&lt;/p&gt;</content:encoded></item><item><title>What you should know before moving to Amsterdam</title><link>https://www.abahgat.com/blog/moving-to-amsterdam</link><guid isPermaLink="true">https://www.abahgat.com/blog/moving-to-amsterdam</guid><description>A practical guide covering everything I had to figure out when I relovated to the Netherlands, covering topics such as the 30% ruling for Highly Skilled Migrants, finding where to live, registering as a resident, getting your accounts and getting around.</description><pubDate>Tue, 20 Nov 2012 12:05:19 GMT</pubDate><content:encoded>&lt;p&gt;This post is a bit unusual for this blog: a few months ago I moved from Milan to Amsterdam (curiously, four years after my &lt;a href=&quot;https://www.abahgat.com/blog/how-long-will-it-take/&quot; title=&quot;How long will it take?&quot;&gt;first visit to the city&lt;/a&gt;) and gathered some advice from friends and websites in the process. That information helped a great deal during my first weeks here and ensured that my move was a smooth one. I believe others in the same situation may benefit from the notes I gathered, so I decided to post them here.&lt;/p&gt;
&lt;p&gt;As an EU citizen with a job offer from a company that has an office in the Netherlands, my transition was quite easy, but there were still some aspects that took me a while to figure out: I will try to cover most of them within this post. For everything else, you may find a lot of useful information in the links at the end of this article.&lt;/p&gt;
&lt;h2 id=&quot;finding-an-apartment&quot;&gt;Finding an apartment&lt;/h2&gt;
&lt;p&gt;I had started looking for apartments to rent a couple of weeks before flying in but soon realized it was an exercise in futility. The rental market is extremely dynamic here: properties that are up for rent one day may be gone the day after and new ones show up daily. Because of the high demand, both agents and landlords tend to ignore messages from anyone who is not already in the city. &lt;strong&gt;Start looking for apartments during the very last days before moving in&lt;/strong&gt;: it will give you an idea about the market prices and you will have some chances of getting an appointment to see the places you want to see before they are rented out.&lt;/p&gt;
&lt;p&gt;Here a few websites you can start from&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.funda.nl/&quot;&gt;Funda&lt;/a&gt; – a website affiliated with the national association of real estate agents, it is one of the most reliable sources as the postings are reviewed and usually backed by an agency. The downside is that it is only in Dutch (you’d better get used to that), but you can Google Translate it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.pararius.com/&quot;&gt;Pararius&lt;/a&gt; – you will generally find almost the same postings you found on Funda, but Pararius is translated in several other languages (I mean the interface: the contents of the posting are likely to be in Dutch, clearly) but sometimes you can find posting by landlords that are not relying on an agency (yet).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://amsterdam.en.craigslist.org/&quot;&gt;Craigslist&lt;/a&gt; – less popular here than in the US, it is still possible to find a good number of listings for Amsterdam. Beware, however, that a good fraction of them are from scammers after your money or your personal details. If you are willing to invest some time to sift though the garbage, you may find some good deal without having to rely on an agency. You may use this &lt;a href=&quot;https://ifttt.com/recipes/67028&quot;&gt;IFTTT recipe&lt;/a&gt; to get email notifications of new posts straight in your inbox.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While there are other websites, they are likely to have the same information you can find on Funda and Pararius.&lt;/p&gt;
&lt;p&gt;Once you are in Amsterdam you can visit agencies in the area(s) you are considering and tell them your requirements and your expectations. Do not overdo by contacting too many of them, though: most agencies have access to the same properties to rent, and they it is not unlikely that they will propose you the same properties you saw yourself on Funda.&lt;/p&gt;
&lt;p&gt;Make sure you visit the areas where you are considering renting: while the city is small and commute is generally significantly shorter than in other European cities, each neighborhood has its own character and perks. Ideally, you will want to see what the place looks like during day and night.&lt;/p&gt;
&lt;p&gt;Depending on where you live now, there are some details you may not pay attention to because they are not as relevant in the city you come from. A few examples&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tram lines&lt;/strong&gt; – check whether there is a tram line running right below your window and the area is popular with tourists. The combination of those two is that you will frequently hear the loud bell noise conductors use to &lt;em&gt;scare people away&lt;/em&gt; (literally) of the tracks. Most houses are built with bedrooms away from the road so the noise may be less of a problem at night, but still you want to know what to expect.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bike stands&lt;/strong&gt; – some neighborhoods seem to have a chronic shortage of bike stands. While usually you may be able to chain your bike to a signpost or a tree, it may still be difficult to find a place to leave it if you come back late at night.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;parking&lt;/strong&gt; – if you are planning on driving a car, bear in mind that finding a place to park in Amsterdam is generally challenging, and usually it is not free. You can apply for a parking permit for the neighborhood you live in, but there may be a wait list several months long.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;shops&lt;/strong&gt; – since there aren’t many malls but plenty of smaller supermarkets, you will not be shopping for groceries once per week, but you will find it easier to make smaller purchases more frequently. Surprisingly, butcher shops, bakeries and greengrocers are cheaper (and the food you find there often tastes better) than supermarkets. Having shops close to your place will make your file easier in this sense.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you find a place you like, it is time to look at how contracts work.&lt;/p&gt;
&lt;h2 id=&quot;understanding-the-contract&quot;&gt;Understanding the contract&lt;/h2&gt;
&lt;p&gt;The laws regarding renting in the Netherlands are very protective of tenants. The market is strictly regulated and aspects like the conditions for breaking the contract, the maximum rent and standard contract conditions have to follow certain rules. I found &lt;a href=&quot;http://www.expatica.com/nl/housing/renting/Fair-rent-for-all-in-the-Netherlands_15396.html&quot;&gt;this article&lt;/a&gt; to be a good source of information about how the whole system works.&lt;/p&gt;
&lt;p&gt;Two important points about the regulations are that, under normal circumstances:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;only the tenant is allowed to break a contract,&lt;/li&gt;
&lt;li&gt;“temporary” contracts are extremely uncommon.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a points system according to which every apartment is given a score depending on many different factors (condition, location and many others), and that score determines the maximum price a landlord is allowed to charge. However, as soon as the score of an apartment exceeds the number points that correspond to about € 650, that apartment is considered to belong to “free market” and the score is not really important anymore. That explains why there is almost no apartment in the € 650 – 900 range: apartments that rent for less than € 650 are usually given to locals who have the patience to apply for rent and go through a wait list, and then there is a second market for expats, who don’t know the system or can’t afford to wait months in order to secure a fair rent. If you have a contract and you are not sure whether it is fair there is a non-profit organization, called &lt;a href=&quot;http://www.wswonen.nl/&quot;&gt;Wijksteunpunten Wonen&lt;/a&gt;, that can help you: its members will review your contract and give you advice on how to proceed.&lt;/p&gt;
&lt;h2 id=&quot;understanding-rent-costs&quot;&gt;Understanding rent costs&lt;/h2&gt;
&lt;p&gt;Rent prices in the Netherlands can be &lt;strong&gt;exclusive or **&lt;/strong&gt;inclusive of expenses**. Exclusive prices work as you expect: you pay the landlord the base rent only, and you have to pay all the service costs yourself. With an inclusive rent, you pay an estimated amount in addition to the base rent as an advance payment for service costs (like energy and gas). In this case, the landlord is expected to keep an account of the actual costs and settle the bill at the end of the each year (refunding you the difference if you paid more or asking you to pay the difference if you paid less), so that you will not have to take care of setting up the contracts for energy, gas and the rest.&lt;/p&gt;
&lt;p&gt;Regardless which option you choose, you will also need to consider that you will be expected to pay all the bills that are in registered in your name (like the taxes for water and for waste disposal). Fees often depend on where you live, you may spend about € 300 per year in addition to the rent. You will start getting those bills by the moment you register as a resident (see the following sections) in your apartment.&lt;/p&gt;
&lt;p&gt;If you rely on an agency, be prepared to pay a commission of roughly one month’s rent at least (generally it is calculated on the base rent, excluding expenses, plus 19% tax). This will effectively mean that you will need to be able to pay roughly three times the rent upon entering the apartment (the commission, the first month, and one month’s rent as a deposit).&lt;/p&gt;
&lt;h2 id=&quot;looking-for-shared-accommodation&quot;&gt;Looking for shared accommodation&lt;/h2&gt;
&lt;p&gt;I have been focusing since the beginning in finding an apartment, but there certainly are plenty of options if you want to share a place with someone else. The two options I have been (shortly) considering are &lt;a href=&quot;http://amsterdam.en.craigslist.org/&quot;&gt;Craigslist&lt;/a&gt; (again, the same considerations in the previous section apply here) and &lt;a href=&quot;http://kamernet.nl/&quot;&gt;Kamernet&lt;/a&gt;, a well-known website dedicated almost exclusively to rooms for rent. Even more than with apartments, being already in Amsterdam helps: you want to meet your possible flatmates in person and, more important, they want to meet you before making a decision. Do not make the mistake of underestimating the reach of your friends and acquaintances: they may know someone who is looking for a flatmate.&lt;/p&gt;
&lt;h2 id=&quot;registering-as-a-resident-and-obtaining-a-bsn&quot;&gt;Registering as a resident and obtaining a BSN&lt;/h2&gt;
&lt;p&gt;Getting a BSN (&lt;a href=&quot;http://en.wikipedia.org/wiki/National_identification_number#Netherlands&quot; title=&quot;National identification number&quot;&gt;Burgerservicenummer&lt;/a&gt;, a sort of social security number) is a necessary step if you are going to live in the Netherlands: you will need it to get your health insurance, to open a bank account and your employer will need it before they can start paying your salary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You will get a BSN by registering as a resident in Amsterdam&lt;/strong&gt; and you are supposed to do so within the fist five days of arrival if you are planning on staying more than four months. If you are a &lt;em&gt;Highly Skilled Migrant&lt;/em&gt; (see following section) the process is extremely simple and you can take care of everything you need in one single day at the &lt;a href=&quot;http://www.iamsterdam.com/en-GB/living/Expatcenter&quot;&gt;Expatcenter&lt;/a&gt;, provided that you asked for appointment &lt;a href=&quot;http://www.iamsterdam.com/en-GB/living/Expatcenter/make-an-appointment/Online-appointment-form-EU-citizens&quot;&gt;here&lt;/a&gt; (applies only to EU citizens).&lt;/p&gt;
&lt;p&gt;You will need to provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your passport (an ID card may suffice, depending on your nationality)&lt;/li&gt;
&lt;li&gt;a rental/tenancy contract or a home purchase agreement&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that, if you have not found permanent accommodation by the time you need to register you may still &lt;strong&gt;register at your employer’s address&lt;/strong&gt;. Doing so, however, requires you company to provide a stamped registration form that explicitly authorizes you to do so. Note that this is considered by all means a contingency situation, and you &lt;strong&gt;will be required to register at your permanent address within three months&lt;/strong&gt;. If you have a work contract already you can get a registration certificate (a sticker on your passport) as well: you will need it if you want to purchase an apartment or vote, for example. Note that to finalize registration you will have to present your birth (and marriage, if applicable) certificate in long form. You will not need this immediately as you can present it later, but if you have not left your country already, you can get one in advance.&lt;/p&gt;
&lt;p&gt;Note that if you are not an EU citizen and/or not have a work contract yet the procedure may differ (even significantly), but luckily the IND (the Immigration and Naturalisation Service) has a &lt;a href=&quot;http://english.ind.nl/residencewizard/&quot;&gt;wizard&lt;/a&gt; you can use to discover what you have to do to get a residence permit depending on your nationality and other conditions. The rules are quite complex but the wizard helps a lot: I suggest you to go through it.&lt;/p&gt;
&lt;p&gt;Registering as a resident will kickstart a series of processes that usually end up in you having to pay some money: taxes on water and waste disposal are usually computed from the day you registered and bills will follow you a few days after you moved in.&lt;/p&gt;
&lt;p&gt;Note that you may also want to register at you local embassy/consulate as a national living abroad. Specifics depend on your country, but the process is usually straightforward.&lt;/p&gt;
&lt;h2 id=&quot;highly-skilled-migrants-and-the-30-ruling&quot;&gt;Highly Skilled Migrants and the 30% ruling&lt;/h2&gt;
&lt;p&gt;The Netherlands apply a particularly attractive policy to foreign workers that meet certain criteria: if you are recruited or transferred from abroad by a Dutch company and have a skill set that is rare or inexistant in the country you may be eligible to a tax reduction.&lt;/p&gt;
&lt;p&gt;In practice, you can apply for the ruling if you earn more than a certain amount (at the time of writing it means € 50,000 if you are older than 30, € 38,000 otherwise) and your employer is willing to certify that you possess rare skills. Note that you can not present the application yourself: your employer has to do it.&lt;/p&gt;
&lt;p&gt;If you present your application within 4 months of the start of employment the tax reduction is retroactive, otherwise it applies from the month after the one you filed your request. If your application is accepted, you will pay at most a bit more than 36% in taxes and will be eligible to a few benefits.  You can find more details on the ruling &lt;a href=&quot;http://www.iamsterdam.com/en-GB/living/official-matters/thirty-percent-ruling&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;opening-a-bank-account&quot;&gt;Opening a bank account&lt;/h2&gt;
&lt;p&gt;Opening a bank account is quite easy: there are less banks here in the Netherlands than there are in other countries and the difference in offering is not significant. If you do not know Dutch yet, you will be interested in banks that are able to deal with expats. The challenge is not in the staff (again, anyone is able to speak good English here) but in the correspondence: you will want to receive communications in a language you understand. &lt;a href=&quot;http://abnamro.nl/expats&quot;&gt;ABN Amro&lt;/a&gt; is quite good in that respect (even though you will still struggle to find some documents in English) and I know that Rabobank has a special expat desk, but I am sure that you may the same with other banks as well. Just ask what they offer to expats and do not assume anything.&lt;/p&gt;
&lt;p&gt;Once you made your choice, you will need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your BSN,&lt;/li&gt;
&lt;li&gt;your passport,&lt;/li&gt;
&lt;li&gt;(usually) a copy of your contract or a letter of employment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that if you don’t have a BSN but you still need to open a bank account, the ABN Amro branch at Schipol Airport may be able to open one for you if you commit to communicate them your BSN as soon as you obtain one. Unfortunately I did not know that at the time, otherwise things would have been much easier.&lt;/p&gt;
&lt;h2 id=&quot;getting-your-health-insurance&quot;&gt;Getting your health insurance&lt;/h2&gt;
&lt;p&gt;Health insurance in the Netherlands is mandatory, even if you already have a one in your country of origin. You will need to get at least a basic insurance package (around € 100 per month) for your company of choice. The contents of a basic package are regulated by law, insurance companies are not allowed to charge different fees depending on demographics and are not allowed to refuse assistance, regardless of pre-existing conditions. Unlike what happens in other countries, you have to pay for your insurance but your employer may provide additional coverage as part of the compensation package. There are many different insurance companies in the Netherlands but, if you are going to get any additional coverage as part of your compensation, you will probably sign with the ones your employer has agreements with.&lt;/p&gt;
&lt;h2 id=&quot;moving-around&quot;&gt;Moving around&lt;/h2&gt;
&lt;p&gt;Amsterdam is a small city, especially compared with Milan, where I used to live until a few months ago. Commute is shorter and there are many different options for moving around.&lt;/p&gt;
&lt;h3 id=&quot;public-transport&quot;&gt;Public transport&lt;/h3&gt;
&lt;p&gt;Public transport in Amsterdam is excellent: the network of trams, buses and subway allows you to get to most destinations in a reasonable amount of time and with a limited number of changes. While the regular service stops a bit after midnight, night buses are still frequent enough to be a realistic option (for everything else, there are taxis). You can get public transport tickets at vending machines that you can find at in all the subway stations, but you can purchase them on board of trams and buses as well. If you do so, you are usually surprised by the costs: € 2.70 for a 1-hour ticket (you can use it as many times as you want) and € 7.50 for a day ticket.&lt;/p&gt;
&lt;p&gt;It is indeed a lot, but there are ways to pay less. If you get an &lt;a href=&quot;http://www.ov-chipkaart.nl/&quot;&gt;OV-chipkaart&lt;/a&gt; you will pay as you go: the amount will be charged will depend on the distance you travel (with an average of about € 1.50 for a trip across the city). You will have to &lt;strong&gt;check by tapping your card against an RFID reader once you board and check out once you get off&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The card itself costs € 7.50 and it can be topped up online, at vending machines at stations and supermarkets. You can get an anonymous card or a personal one which will have your name and photo on it. The advantage of the latter is that you will be able to use it for seasonal passes or have it top up itself automatically with credit from your bank account.&lt;/p&gt;
&lt;p&gt;You can travel on trains across all the Netherlands using the same chipkaart you use for public transport in the city, you will only have to visit one of the &lt;a href=&quot;http://www.ns.nl/&quot;&gt;NS&lt;/a&gt; vending machines (the yellow ones) and select your preference between 1st and 2nd class.&lt;/p&gt;
&lt;h3 id=&quot;biking&quot;&gt;Biking&lt;/h3&gt;
&lt;p&gt;Bikes are a symbol for the Netherlands, and you will find Amsterdam to be perfectly designed for cyclists. Locals prefer biking to any other alternative, and you will find people driving their bike to work regardless of the weather. Once you will have spent more than a few days here you will be inclined to do the same: if you spend most of your time within the city, biking is faster than any alternative.&lt;/p&gt;
&lt;p&gt;Get a sturdy bike, as it will need to withstand harsh weather conditions. Do not buy a fancy bike, as it will get more than a few scratches when placed on bike racks (even if you pay attention to avoid that, people placing their bike next to yours will not be as careful as you). Buy a good lock, even if it costs a lot compared to the value of the bike, especially if you leave your bike on the street at night: crime rate in Amsterdam is lower than in other cities, but &lt;strong&gt;bike theft is incredibly common&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;driving&quot;&gt;Driving&lt;/h3&gt;
&lt;p&gt;Owning a car in Amsterdam is expensive: the road tax is quite high and parking is very limited (bear in mind that even if you have a permit to park in your neighborhood, you will likely need to pay if you drive to another). Given the way the canals affect the road plan, traversing the city by car is generally not the quickest option. Should you decide to bring your own car, you will have to follow a strict procedure: you can find more information &lt;a href=&quot;http://www.expatica.com/nl/leisure/travel_tourism/a-guide-to-car-use-in-the-netherlands-8855_9070.html&quot;&gt;here&lt;/a&gt;. Depending on the country you come from, you may also have to &lt;a href=&quot;http://www.iamsterdam.com/en-GB/living/official-matters/driving-licence&quot;&gt;exchange your driving licence&lt;/a&gt; with a Dutch one or get a new one.&lt;/p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful resources&lt;/h2&gt;
&lt;p&gt;I summarized all the information in my possession but I have not covered some topics that may still be of interest to you. The links in this section should provide you with all the information you need.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.iamsterdam.com/en-GB/living&quot;&gt;I amsterdam&lt;/a&gt; – by far the most complete and accurate source of information about what you need to know about moving to Amsterdam&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://english.ind.nl/&quot;&gt;IND&lt;/a&gt; – the official website of the Immigration and Naturalisation Service&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.internations.org/amsterdam-expats/guide/moving-to-amsterdam-15398&quot;&gt;Moving to Amsterdam&lt;/a&gt; – a useful section on Internations that contains some more useful information (access is restricted to members, unfortunately)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to that, if you are already in the Netherlands or can plan a visit before moving, consider visiting the &lt;a href=&quot;http://www.iamsterdam.com/en-GB/living/Expatcenter/contact-us&quot;&gt;Expatcenter&lt;/a&gt; in person: they will be able to help you and give a clear picture about what you need to do in order to move here.&lt;/p&gt;
&lt;a href=&quot;http://www.amazon.com/Why-The-Dutch-Are-Different-ebook/dp/B014GBMMOA/ref=as_li_ss_il?ie=UTF8&amp;linkCode=li2&amp;tag=theunti-20&amp;linkId=93b067bd2c0ddcc1a85b314b661085f3&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;alignleft&quot; src=&quot;//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;#38;ASIN=B014GBMMOA&amp;#38;Format=_SL160_&amp;#38;ID=AsinImage&amp;#38;MarketPlace=US&amp;#38;ServiceVersion=20070822&amp;#38;WS=1&amp;#38;tag=theunti-20&quot; alt border=&quot;0&quot;/&gt;&lt;/a&gt;
&lt;img style=&quot;border:none !important;margin:0!important;&quot; src=&quot;//ir-na.amazon-adsystem.com/e/ir?t=theunti-20&amp;#38;l=li2&amp;#38;o=1&amp;#38;a=B014GBMMOA&quot; alt width=&quot;1&quot; height=&quot;1&quot; border=&quot;0&quot;/&gt;
&lt;p&gt;&lt;strong&gt;Bonus&lt;/strong&gt;: Here’s a book I’d recommend if you want to learn more about the Dutch and their culture: &lt;a href=&quot;http://amzn.to/1RqMrC3&quot;&gt;Why The
Dutch Are Different: A Journey Into the Hidden Heart of the Netherlands&lt;/a&gt;. It will be a good way to prepare to what
you’ll find once you move there.&lt;/p&gt;
&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements 🙂&lt;/h2&gt;
&lt;p&gt;Most of the information in this article comes from personal research but, even more, by friends that have been through the experience of relocating to Amsterdam. Their precious advice helped me making my move here smooth and painless. &lt;strong&gt;Thank you Andrea, Angelo, Matias, Nicola.&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title>On desire paths… and software</title><link>https://www.abahgat.com/blog/on-desire-paths-and-software</link><guid isPermaLink="true">https://www.abahgat.com/blog/on-desire-paths-and-software</guid><description>Desire paths are a common pattern in landscape design: born as simple footpaths when someone takes a more direct, shorter way to their destination, they often evolve to proper paths and roads after a ...</description><pubDate>Mon, 23 Jul 2012 13:10:00 GMT</pubDate></item></channel></rss>