<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Skip Blog</title>
        <link>https://skiplabs.io/blog</link>
        <description>Skip Blog</description>
        <lastBuildDate>Wed, 03 Jun 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Verify the description, not the effect]]></title>
            <link>https://skiplabs.io/blog/side_effects</link>
            <guid>https://skiplabs.io/blog/side_effects</guid>
            <pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How does a closed-loop coding agent verify a side effect it can't cheaply or totally observe? By checking the effect's description against a fixed contract, not the effect itself.]]></description>
            <content:encoded><![CDATA[<p>Of all the questions under the launch post, one actually mattered. How does a closed loop close on side effects? Sending an email. Logging to Sentry. And the harder version: calling an internal service at a big company, where there's no public sandbox and no shared test environment to point at.</p>
<p>It's the right question, because it's the one that should break the pitch if we got the architecture wrong. A verify-loop is convincing when the thing it checks is cheap and total to check. Side effects are neither.</p>
<p>So let me answer it honestly. Which means starting with what we don't do.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-honest-part-first">The honest part first<a href="https://skiplabs.io/blog/side_effects#the-honest-part-first" class="hash-link" aria-label="Direct link to The honest part first" title="Direct link to The honest part first" translate="no">​</a></h2>
<p>When we say Skipper <a href="https://skiplabs.io/blog/closed_loop_coding_agent" target="_blank" rel="noopener noreferrer" class="">closes the loop</a>, we mean it the way control theory means it: the feedback signal has to be trustworthy enough to act on.</p>
<p>Type-checking and deterministic execution give us that. They're cheap to run, and the verdict is total. The check passes for every input or it doesn't, and we don't have to believe a story about it.</p>
<p>A side effect gives us none of that. You can't cheaply verify that an email arrived. You can't verify it totally either, because the answer lives on someone else's mail server on a Tuesday.</p>
<p>So the loop doesn't check whether the effect happened. It checks whether the effect was described correctly. That distinction is the whole post.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="we-already-do-this-its-called-a-compiler">We already do this. It's called a compiler.<a href="https://skiplabs.io/blog/side_effects#we-already-do-this-its-called-a-compiler" class="hash-link" aria-label="Direct link to We already do this. It's called a compiler." title="Direct link to We already do this. It's called a compiler." translate="no">​</a></h2>
<p>We didn't invent this for AI. Compilers have done it for 50 years.</p>
<p>A C compiler verifies that your call to <code>send()</code> matches its declared signature, every argument type and the return shape, and it does that without ever opening a socket. It has never once sent a packet to find out whether your code is correct. That's the whole trick.</p>
<p><a href="https://skiplabs.io/blog/codegen_as_compiler" target="_blank" rel="noopener noreferrer" class="">Treat Agent Output Like Compiler Output</a> argued we should treat agent output the same way: stop reading each artifact by hand, build the verification into the process that produces it. Side effects are where that argument has to prove it isn't hand-waving. They're the one place people assume the compiler move can't reach. The three pieces below are what reaching looks like.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-effects-are-typed-values">1. Effects are typed values<a href="https://skiplabs.io/blog/side_effects#1-effects-are-typed-values" class="hash-link" aria-label="Direct link to 1. Effects are typed values" title="Direct link to 1. Effects are typed values" translate="no">​</a></h2>
<p>Anything that touches the outside world (an email send, an HTTP call, a hash, a database write) isn't an opaque call the model drops wherever it likes. You declare it up front as a typed <em>external</em>: a fixed contract of inputs, outputs, and params.</p>
<p>There's a rule the reactive core enforces with no exceptions. The core can't do I/O at all. The deterministic, reactive part of a Skipper service never touches the network. An effect executes in exactly one sanctioned place: a separate subprocess at the edge, reached through a generated wrapper.</p>
<p>So the loop's job here is narrow and decidable. It checks that the constructed effect type-checks against its contract. Execution gets deferred across the boundary. The model describes the email send. It can't smuggle a socket into the part of the program we're trying to prove things about.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-the-sandbox-runs-against-typed-doubles">2. The sandbox runs against typed doubles<a href="https://skiplabs.io/blog/side_effects#2-the-sandbox-runs-against-typed-doubles" class="hash-link" aria-label="Direct link to 2. The sandbox runs against typed doubles" title="Direct link to 2. The sandbox runs against typed doubles" translate="no">​</a></h2>
<p>For the loop to run a service end to end, the boundary needs something on the other side. That something is a stub. Each external call gets pinned to a typed mock record: given these params, return this value, or this error.</p>
<p>An email send becomes <code>called with {to, subject, body}, returns sent</code>, and the assertion lives in the handler that consumes the result. The model's call site has to match the declared intent or the test fails. You can't hide a malformed payload, because the payload's shape is part of the contract.</p>
<p>Reactive computation, the <a href="https://skiplabs.io/blog/cache_invalidation" target="_blank" rel="noopener noreferrer" class="">Skip Runtime</a> the whole thing runs on, makes those outputs reproducible from the same inputs. Same inputs, same graph, same result, every run. So the verification pass is deterministic without ever touching the network. We're not flaking on some third-party rate limit while trying to decide whether the code is correct. We keep those problems apart on purpose.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-the-contract-is-the-unit-of-verification">3. The contract is the unit of verification<a href="https://skiplabs.io/blog/side_effects#3-the-contract-is-the-unit-of-verification" class="hash-link" aria-label="Direct link to 3. The contract is the unit of verification" title="Direct link to 3. The contract is the unit of verification" translate="no">​</a></h2>
<p>Here's where the big-company question gets its real answer.</p>
<p>You hand Skipper an OpenAPI spec, or a prompt it derives one from, and from that point, for that run, the spec is frozen. It's the fixed reference. The agent codes against it. The loop type-checks against it, unit-tests against it, validates every call site against it. The typed stubs derived from the externals are what let the generated code actually run.</p>
<p>Look at what that does to the "no cross-company sandbox" problem. It dissolves it. There was never going to be a live internal service inside the verification loop, and there doesn't need to be. The interface is fixed, so the model's stochasticity is contained even when the implementation is remote, proprietary, and sitting behind 3 VPNs you've never heard of.</p>
<p>Verification happens against the contract. The live service never enters the loop, so its absence costs you nothing. You need their types, and that's it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-loop-will-not-tell-you">What the loop will not tell you<a href="https://skiplabs.io/blog/side_effects#what-the-loop-will-not-tell-you" class="hash-link" aria-label="Direct link to What the loop will not tell you" title="Direct link to What the loop will not tell you" translate="no">​</a></h2>
<p>Here's the part I'd rather say out loud than have you find out later.</p>
<p>The loop does not claim the effect succeeded in production. It doesn't promise the email reached an inbox, that Sentry ingested the event, that the internal service was even reachable when your code ran for real. Delivery, idempotency, retries: that's the subprocess at the edge running against real credentials, outside the verification loop.</p>
<p>We keep that integration layer visible and separate instead of folding it into the word "verified." I'm doing that on purpose. A closed loop with a dishonest sensor is just an unstable system with extra steps, flying the plane confidently into the ground.</p>
<p>"Verified" has to mean exactly one thing, or it means nothing. Here it means the code is type-correct against the contracts and runs deterministically against them. Whether the third party 3 time zones away is having a good day is a separate question, and the loop doesn't pretend to answer it.</p>
<p>The model is stochastic. The pipe around it isn't. Side effects don't change that. Quietly pretending the loop covers delivery would, so it doesn't, and it says so.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SkipLabs Launches Skipper, The Runtime for the Era of AI-Generated Software]]></title>
            <link>https://skiplabs.io/blog/press_release</link>
            <guid>https://skiplabs.io/blog/press_release</guid>
            <pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[From the creator of Hack, the language behind Facebook’s business logic, comes a closed-loop coding agent that turns one prompt into running software.]]></description>
            <content:encoded><![CDATA[<p>From the creator of Hack, the language behind Facebook’s business logic, comes a closed-loop coding agent that turns one prompt into running software.</p>
<p><strong>PARIS, FRANCE — June 1, 2026</strong> — SkipLabs today launched Skipper, a closed-loop coding agent that takes a single prompt and returns a running, validated service. Skipper was built by Julien Verlaguet, creator of Facebook's Hack programming language, alongside a team of engineers drawn from Facebook, Microsoft, Microsoft Research, Meta and the broader systems community. It serves as the architectural layer sitting between AI models and shipped software.
Skipper is designed specifically for technical builders: product managers running their own AI workflows, engineering teams shipping production services, and developers who demand architectural discipline without architectural overhead.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-missing-link-in-ai-coding">The Missing Link in AI Coding<a href="https://skiplabs.io/blog/press_release#the-missing-link-in-ai-coding" class="hash-link" aria-label="Direct link to The Missing Link in AI Coding" title="Direct link to The Missing Link in AI Coding" translate="no">​</a></h2>
<p>Skipper arrives at a critical inflection point. While AI coding tools have made the first draft of a service nearly free, what remains is the gap between almost running and actually running in production. This is an architecture problem—one that doesn't disappear just because a language model gets smarter.</p>
<blockquote>
<p>"Building correct software has always been an architecture problem disguised as a coding problem," said Julien Verlaguet, Founder and CEO of SkipLabs. "AI did not change that; it just made the problem more urgent."
Skipper sits beneath the foundation models. Rather than competing with Claude, GPT, or Gemini, it acts as the substrate that turns their output into finished software rather than a draft awaiting review. When a builder describes the service they want, Skipper handles the entire pipeline autonomously: decomposing the work into manageable pieces, routing each piece to the specific AI model best suited for that task, generating and validating the code, and returning a reliable, running service. No developer review, no oversight cycles, no back-and-forth.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="fixing-state-and-concurrency">Fixing State and Concurrency<a href="https://skiplabs.io/blog/press_release#fixing-state-and-concurrency" class="hash-link" aria-label="Direct link to Fixing State and Concurrency" title="Direct link to Fixing State and Concurrency" translate="no">​</a></h2>
<p>State management and concurrency are the hardest parts of writing correct software, and the exact areas where AI most often produces broken code.
Skipper solves this via its reactive runtime—drawn from the open-source Skip framework Verlaguet built at Facebook in 2017. Because the runtime automatically handles state management and concurrency, the AI model never has to reason about cause and effect across a sprawling state graph. By shrinking the surface area where AI can make mistakes, Skipper ensures more of what it produces actually works.</p>
<blockquote>
<p>"Closed loop is not a feature," Verlaguet added. "It is a different theory of what an AI coding tool is supposed to do. The current generation makes the developer faster. The next generation makes the developer's involvement optional. Describe what you want and Skipper builds it."</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-ships-today">What Ships Today<a href="https://skiplabs.io/blog/press_release#what-ships-today" class="hash-link" aria-label="Direct link to What Ships Today" title="Direct link to What Ships Today" translate="no">​</a></h2>
<p>Launching today, Skipper delivers the core closed-loop experience: a single prompt becomes a running service. A model-agnostic harness routes each task to the AI model best suited for it, with no lock-in to any single foundation model. And Skipper-built services integrate with the outside world from day one — calling external APIs, fetching live data, and posting to other systems to ensure the software fits into a real stack, not just a sandbox.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-product-roadmap">The Product Roadmap<a href="https://skiplabs.io/blog/press_release#the-product-roadmap" class="hash-link" aria-label="Direct link to The Product Roadmap" title="Direct link to The Product Roadmap" translate="no">​</a></h2>
<p>In the weeks following launch, SkipLabs will roll out SKJS, a sound, TypeScript-compatible type system that lets Skipper understand how changes to one part of the code affect the rest.  Next comes in-place incremental updates, allowing builders to change a specification and have Skipper modify the running service without a full rebuild. By late 2026, SkipLabs will deliver precise, zero-pollution awareness of the full codebase at any scale.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="who-skipper-is-for">Who Skipper is For<a href="https://skiplabs.io/blog/press_release#who-skipper-is-for" class="hash-link" aria-label="Direct link to Who Skipper is For" title="Direct link to Who Skipper is For" translate="no">​</a></h2>
<p>Skipper is built for those who experience the frustration of watching an AI generate a near-working idea, yet possess the technical knowledge to see exactly why the last 10% breaks. It is designed for technical product managers wiring AI into production tools via real API integrations rather than sandboxed demos. It also serves engineering teams shipping production backend services who want architectural discipline without the per-line review cycle, and software development firms building AI-driven systems for clients whose code absolutely must ship.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-decade-making-code-provably-correct">A Decade Making Code Provably Correct<a href="https://skiplabs.io/blog/press_release#a-decade-making-code-provably-correct" class="hash-link" aria-label="Direct link to A Decade Making Code Provably Correct" title="Direct link to A Decade Making Code Provably Correct" translate="no">​</a></h2>
<p>The architecture beneath Skipper predates the current AI coding wave and is backed by over a decade of infrastructure-first philosophy. Verlaguet created Hack, the gradually typed programming language that powers Facebook's business logic and over 100 million lines of production code. Amplify Partners, which led SkipLabs' $8 million seed round, noted in its investment memo that Verlaguet is <em>"one of the top two to three programming language designers in the world."</em> His work on programming language design has also been publicly endorsed by Turing Award winner and former Meta Chief AI Scientist Yann LeCun.
<strong>Skipper is available today at <a href="https://skipperai.dev/" target="_blank" rel="noopener noreferrer" class="">skipperai.dev</a>.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-skiplabs">About SkipLabs<a href="https://skiplabs.io/blog/press_release#about-skiplabs" class="hash-link" aria-label="Direct link to About SkipLabs" title="Direct link to About SkipLabs" translate="no">​</a></h2>
<p>SkipLabs builds the infrastructure layer that makes AI-generated code work in production. Founded in 2022 by Julien Verlaguet, creator of Facebook's Hack programming language, the company develops tools that close the loop between intent and running software for technical product managers, engineering teams, and the firms that build production systems on top of AI. SkipLabs is backed by Amplify Partners, with angel investors including Yann LeCun and Spencer Kimball, co-founder and CEO of Cockroach Labs. For more information, visit <a href="https://skiplabs.io/" target="_blank" rel="noopener noreferrer" class="">skiplabs.io</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What a Closed-Loop Coding Agent Actually Is]]></title>
            <link>https://skiplabs.io/blog/closed_loop_coding_agent</link>
            <guid>https://skiplabs.io/blog/closed_loop_coding_agent</guid>
            <pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Closed-loop control requires a feedback signal trustworthy enough to act on. Most AI coding agents close their loops on lossy sensors, tests, unsound types, model judgment, and inherit the instability that follows. Skipper is built on sound signals with SKJS, deterministic execution, and reactive computation.]]></description>
            <content:encoded><![CDATA[<p>Skipper is a closed-loop coding agent. That label carries more weight than it appears. The industry adopted "closed-loop" without retaining its original meaning, so many tools labeled that way don't truly qualify. Once you understand the distinction, you see what sets Skipper apart.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-closed-loop-meant-before-we-borrowed-it">What closed-loop meant before we borrowed it<a href="https://skiplabs.io/blog/closed_loop_coding_agent#what-closed-loop-meant-before-we-borrowed-it" class="hash-link" aria-label="Direct link to What closed-loop meant before we borrowed it" title="Direct link to What closed-loop meant before we borrowed it" translate="no">​</a></h2>
<p>Closed-loop control comes from control theory. An autopilot reads altitude, compares it to the target, nudges the elevators based on the error. The defining property isn't that a loop exists. It's that <strong>the feedback signal is trustworthy enough to act on</strong>.</p>
<p>An autopilot with a broken altimeter will fly the plane into the ground. Confidently. Autonomously. In perfect mechanical closure.</p>
<p>Control theorists know this. They obsess over sensor fidelity, because a closed loop with a bad sensor isn't a stable system. It's an unstable one with extra steps.</p>
<!-- -->
<p>Medical devices go further. An artificial pancreas is closed-loop. A glucose monitor that beeps at a human to dose insulin is not. The line isn't drawn at the loop. It's drawn at whether the signal is sound enough to take the human out. You earn closed-loop autonomy through signal quality. You can't declare it through architecture.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-most-agent-loops-actually-close-on">What most agent loops actually close on<a href="https://skiplabs.io/blog/closed_loop_coding_agent#what-most-agent-loops-actually-close-on" class="hash-link" aria-label="Direct link to What most agent loops actually close on" title="Direct link to What most agent loops actually close on" translate="no">​</a></h2>
<p>So look at what a typical coding agent feeds back into its next decision:</p>
<ul>
<li class=""><strong>Test output.</strong> Tests are samples, not proofs. The agent can pass them without satisfying the intent. Sometimes by editing the test.</li>
<li class=""><strong>Linter and type-checker output.</strong> Useful, but TypeScript's type system is unsound by design. A green check means "I couldn't find a contradiction in the bit I can see." It doesn't mean correct.</li>
<li class=""><strong>Runtime logs.</strong> Noisy, incomplete, silent on the failures you actually care about.</li>
<li class=""><strong>Model-based judgment.</strong> The agent's own confidence, or a second model with the same blind spots. Either way, the signal is the thing you were trying to check.</li>
</ul>
<p>Every one of these is a lossy sensor. It drops information about the thing it's supposed to measure. The loop still closes mechanically. Output feeds back into the next decision. But what flows through it is a low-fidelity proxy for correctness.</p>
<p>And because the agent acts on that proxy with full autonomy, small signal errors compound into the failure modes we all recognize:</p>
<ul>
<li class="">The agent deletes the failing test and declares victory.</li>
<li class="">The agent ships code that type-checks fine but violates an invariant the types can't express.</li>
<li class="">The agent loops for 40 minutes polishing a solution that was already correct on attempt 3, because nothing in the signal told it to stop.</li>
<li class="">The agent drifts. Each step is locally plausible. The trajectory is nonsense.</li>
</ul>
<p>These aren't bugs in the agents. They're what closed-loop control does when the sensor is lossy. Control theory called this decades ago. We're rediscovering it the hard way.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-physics-problem-not-a-craftsmanship-problem">A physics problem, not a craftsmanship problem<a href="https://skiplabs.io/blog/closed_loop_coding_agent#a-physics-problem-not-a-craftsmanship-problem" class="hash-link" aria-label="Direct link to A physics problem, not a craftsmanship problem" title="Direct link to A physics problem, not a craftsmanship problem" translate="no">​</a></h2>
<p>Here's the part worth sitting with. No amount of prompt engineering, scaffolding, or agent-framework cleverness fixes a lossy feedback signal. You can tune the controller all day. If the altimeter is wrong, the altitude is wrong. The ceiling on an agent's reliability is the trustworthiness of what it closes the loop on.</p>
<p>So the real question when you're evaluating an AI coding tool isn't <em>how autonomous is the loop</em>. It's <em>what does the loop close on, and how much can you trust it?</em></p>
<p>A sound type checker closes a better loop than an unsound one. A deterministic execution environment closes a better loop than a flaky one. A reactive system that recomputes only what changed closes a better loop than one that re-runs everything and re-derives confidence from noise. These are sensor upgrades. They're what lets the loop actually control something.</p>
<p>This is what Skipper is built on. SKJS is a fully sound TypeScript-compatible type checker, so when Skipper's loop closes on a type signal, that signal means what it says. Execution is deterministic, so "it worked" is reproducible instead of probabilistic. Computation is reactive, so feedback is precise instead of a fog of re-runs. Three sensor upgrades. Together they're what lets Skipper close the loop at all.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-matters">Why this matters<a href="https://skiplabs.io/blog/closed_loop_coding_agent#why-this-matters" class="hash-link" aria-label="Direct link to Why this matters" title="Direct link to Why this matters" translate="no">​</a></h2>
<p>The industry has been iterating hard on the controller. Better prompts, better scaffolds, better planning. And treating the feedback signal as given. The feedback signal was never given. It's the variable that decides whether your autonomous loop is closed-loop or just an unstable one in a costume.</p>
<p>Skipper closes the loop because Skipper took the sensor seriously. Everything else in the category is running open-loop with confidence it hasn't earned.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The apparatus, not the artifact]]></title>
            <link>https://skiplabs.io/blog/notes_from_the_comments</link>
            <guid>https://skiplabs.io/blog/notes_from_the_comments</guid>
            <pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A follow-up to "Treat Agent Output Like Compiler Output" — addressing the responses, why determinism isn't the point, and what static analysis actually buys you.]]></description>
            <content:encoded><![CDATA[<p><em>A follow-up to <a href="https://skiplabs.io/blog/codegen_as_compiler" target="_blank" rel="noopener noreferrer" class="">"Treat Agent Output Like Compiler Output"</a>, published in March.</em></p>
<p>That essay argued that the right response to AI-generated code isn't more code review — it's the same trick the industry already pulled with compilers: build the verification <em>into the process that produces the output</em>, then stop reviewing each artifact by hand.</p>
<p>The internet had thoughts. Or at least X had thoughts... a lot of them, often in flowery language, as you can imagine. I read every one. Most of them landed on three or four arguments worth addressing head-on.</p>
<p>Before I go through them, I want to restate the thesis, because most of the responses were answering a question I didn't ask.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="restating-the-take">Restating the take<a href="https://skiplabs.io/blog/notes_from_the_comments#restating-the-take" class="hash-link" aria-label="Direct link to Restating the take" title="Direct link to Restating the take" translate="no">​</a></h2>
<p>The point of the original piece was not that LLMs <em>are</em> compilers. The point was that fifty years of compiler R&amp;D — type systems, static analysis, formal semantics, abstract interpretation, symbolic execution, model checking — is exactly the apparatus we now need to wrap around coding agents.</p>
<p>Agents on their own cannot be trusted to ship code. They need a straightjacket. Static analysis is the straightjacket. The compiler community spent five decades building tools that prove things about code <em>before</em> it runs. That toolkit is the answer. Agents need it more than humans ever did, not less.</p>
<p>The model is stochastic. The pipe around it isn't.</p>
<p>The analogy isn't "agents are compilers." The analogy is "the R&amp;D tradition that produced the trust apparatus around compilers is the R&amp;D tradition I need to point at agents now."</p>
<p>With that on the table, let me go through what landed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-critics-got-right">What the critics got right<a href="https://skiplabs.io/blog/notes_from_the_comments#what-the-critics-got-right" class="hash-link" aria-label="Direct link to What the critics got right" title="Direct link to What the critics got right" translate="no">​</a></h2>
<p>Two objections genuinely move the piece, and both of them make the case for static analysis <em>stronger</em>, not weaker.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hallucination-has-no-compiler-analogue">Hallucination has no compiler analogue<a href="https://skiplabs.io/blog/notes_from_the_comments#hallucination-has-no-compiler-analogue" class="hash-link" aria-label="Direct link to Hallucination has no compiler analogue" title="Direct link to Hallucination has no compiler analogue" translate="no">​</a></h3>
<p>The cleanest version of the point, paraphrased from one of the more direct replies: compilers don't hallucinate, and they don't recommend functions that don't exist.</p>
<p>Right. This is the failure mode that doesn't exist in the compiler world, and I should have addressed it explicitly. A compiler translates a formally specified input into a formally specified output. An LLM produces semantics, sometimes out of thin air.</p>
<p>But notice what catches a hallucinated function call: static analysis. What catches a reference to a nonexistent symbol: type checking and name resolution specifically — passes that have shipped in every serious compiler since the 1970s. Hallucination is exactly the failure mode this machinery was built to catch. The agent will invent a function; a sound type system will reject the program before it runs. That's the apparatus doing its job.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-reliability-gap-is-real-and-its-the-entire-engineering-challenge">The reliability gap is real, and it's the entire engineering challenge<a href="https://skiplabs.io/blog/notes_from_the_comments#the-reliability-gap-is-real-and-its-the-entire-engineering-challenge" class="hash-link" aria-label="Direct link to The reliability gap is real, and it's the entire engineering challenge" title="Direct link to The reliability gap is real, and it's the entire engineering challenge" translate="no">​</a></h3>
<p>The <em>failure-rate</em> gap between a modern C compiler and a frontier LLM isn't 1.5× or 10×. It's many orders of magnitude. Compilers fail rarely enough that individual miscompilations are notable events, tracked, and fixed across the toolchain. LLMs fail often enough that "did the model hallucinate" is a routine line item in any agent pipeline.</p>
<p>This is not an argument against the analogy. It is the argument <em>for</em> aggressive static guarantees on agent output. When the author fails at parts-per-million, you can get away with light verification. When the author fails at parts-per-hundred, you cannot. The reliability gap is precisely why every tool the compiler community has produced — and several it hasn't yet — needs to be pointed at coding agents now.</p>
<p>Both of these objections are correct. Both of them strengthen the case for treating agent output as something to be verified by serious infrastructure, not eyeballed by a human.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-critics-got-right-that-doesnt-touch-the-argument">What the critics got right that doesn't touch the argument<a href="https://skiplabs.io/blog/notes_from_the_comments#what-the-critics-got-right-that-doesnt-touch-the-argument" class="hash-link" aria-label="Direct link to What the critics got right that doesn't touch the argument" title="Direct link to What the critics got right that doesn't touch the argument" translate="no">​</a></h2>
<p>A second cluster of objections is technically true and rhetorically beside the point.</p>
<p><strong>"Compilers have formal specifications. LLMs don't."</strong> True. The analogy is not "LLMs are formally specified like compilers." The analogy is that the apparatus I built around compilers — much of which doesn't depend on the <em>compiler itself</em> being formally specified — is what agents need. Type checkers, sanitizers, fuzzers, model checkers: most of this apparatus operates on the <em>output</em> program, not on the source-to-machine translation. It would be useful even if the upstream artifact had no spec at all. Which is convenient, because in the agent case the upstream artifact doesn't.</p>
<p><strong>"Humans actually do read compiler output."</strong> Yes — in embedded, GPU, security, and performance-critical contexts. The honest version of the original claim is narrower: <em>most code shipped in production today is not produced by a human reading the assembly first</em>, and that's possible because of the apparatus. I should have written the narrower version. But this correction is about scope, not about the structure of the argument.</p>
<p><strong>"Compilers are extremely reliable. LLMs constantly fail."</strong> Already covered above. Yes — and this is why the apparatus matters more, not less.</p>
<p>These are real points. They're not the points the determinism arguments were trying to make.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-the-critics-missed">Where the critics missed<a href="https://skiplabs.io/blog/notes_from_the_comments#where-the-critics-missed" class="hash-link" aria-label="Direct link to Where the critics missed" title="Direct link to Where the critics missed" translate="no">​</a></h2>
<p>The single largest cluster of objections — and it was a <em>very</em> large cluster — was effectively one critique repeated three ways: <em>compilers are deterministic, LLMs aren't, therefore the analogy fails.</em></p>
<p>This is arguing about a property of the artifact. The original piece was arguing about the apparatus around the artifact. They are different things, and conflating them is the actual category error.</p>
<p>You don't trust a compiled binary because the compiler is deterministic. You trust it because of types, tests, fuzzers, sanitizers, ABI checks, monitoring, and rollback. Most of that apparatus would be useful even if the compiler weren't deterministic — which is convenient, because in practice it often isn't. Compiler output shifts under <a href="https://en.wikipedia.org/wiki/Profile-guided_optimization" target="_blank" rel="noopener noreferrer" class="">PGO</a>, <a href="https://en.wikipedia.org/wiki/Interprocedural_optimization" target="_blank" rel="noopener noreferrer" class="">LTO</a>, parallel link order, optimization-level changes, GPU <a href="https://en.wikipedia.org/wiki/Floating-point_arithmetic" target="_blank" rel="noopener noreferrer" class="">floating-point</a> reordering, and timestamps. Nobody has decided compilers are therefore not analogous to themselves. The apparatus absorbs the non-determinism.</p>
<p>The validators, the type checkers, the test runners, the static analyzers, the CI gates: those are generally not deterministic either (some may be), and none of them are individually robust. What they are is a stack — each one trims a class of failure, and together they push the failure rate of the <em>output</em> close enough to zero that you trust the artifact. <em>Build the stack.</em> The model is stochastic. The pipe around it doesn't have to be deterministic — it has to be robust enough that the artifact is.</p>
<p>The "just use temperature zero" variant of the criticism misses in the other direction. Greedy decoding with a fixed seed gets you bit-identical output for a single prompt at a single checkpoint. It does not get you a deterministic function from natural-language intent to working code. The variance lives upstream, in the prompt, the retrieval context, the tool transcripts. You can pin the sampler and still get a different program tomorrow. Sampler determinism is not the determinism property anyone actually wants.</p>
<p>If your response to "build the verification apparatus" is "but LLMs aren't deterministic," you're describing why the apparatus is <em>harder</em> to build, not why you shouldn't build it.</p>
<p>Worth noting: since the original post went up, several of the people running the determinism critique have publicly walked it back. The strongest correction came from inside the LLVM community — the observation that real-world compilers are wildly non-deterministic by default. Internal passes use unordered containers keyed on pointer addresses, which shift every run thanks to <a href="https://en.wikipedia.org/wiki/Address_space_layout_randomization" target="_blank" rel="noopener noreferrer" class="">ASLR</a>. Non-stable sorting algorithms reorder things between invocations. Filesystem inode allocation dictates enumeration order. Reproducible builds are a massive engineering effort precisely because the underlying toolchain is so far from deterministic to begin with. The mathematical theory of a compiler is deterministic. The actual production toolchain is not. This is exactly the distinction the original piece was trying to make, made better by people who initially thought I was wrong about it.</p>
<p>The most rigorous version of the rebuttal came from outside the thread entirely. Isaac Van Doren's <a href="https://isaacvando.com/nondeterminisms-not-the-problem" target="_blank" rel="noopener noreferrer" class=""><em>Nondeterminism's not the problem</em></a> makes the point cleanly: imagine a compiler that picks register assignments by <code>Math.random</code> — it would still be useful. Then set an LLM to temperature zero — it's still untrustworthy. Determinism is irrelevant. The real difference is that programming languages have semantics and prompts don't. I agree, and that reframe is the spine of the next section.</p>
<p>The position the debate has settled into is the right one: compilers are not deterministic, LLMs are <em>more</em> non-deterministic than compilers, and the gap is large.</p>
<p>That gap is the engineering challenge, it is not a category boundary.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-static-analysis-actually-buys-you">What static analysis actually buys you<a href="https://skiplabs.io/blog/notes_from_the_comments#what-static-analysis-actually-buys-you" class="hash-link" aria-label="Direct link to What static analysis actually buys you" title="Direct link to What static analysis actually buys you" translate="no">​</a></h2>
<p>The argument so far is abstract. Here's the concrete version.</p>
<p>The underlying problem is straightforward: programming languages have semantics; prompts don't. The nearly 900 pages of the Java Language Specification tell you exactly what promises the language makes about your code's behavior. A prompt makes no promises. To give a prompt the kind of guarantee a language gives you, you would need an external tool to validate that the output upholds the prompt's intended semantics — and the cost of doing that at the level of full theorem proving is famously prohibitive.</p>
<p>The honest version is more interesting. Programming languages don't actually specify everything either. Java doesn't tell you when the garbage collector runs. Haskell doesn't tell you what gets evaluated next — that's a function of the runtime, not the language. C doesn't tell you whether a variable lives on the stack or in a register. These languages succeed not because their specifications are exhaustive, but because the apparatus around them — the GC, the runtime, the register allocator — makes the underspecified parts irrelevant to the correctness of your program. The abstraction holds.</p>
<p>That's the move we need for agents. The prompt is underspecified, and at one level it always will be. The apparatus has to make that underspecification stop mattering — to enforce, at the level of the <em>output program</em>, the properties the prompt couldn't pin down.</p>
<p>You don't need full theorem proving to get most of the way there. The compiler R&amp;D tradition has produced a layered stack of tools that impose semantic guarantees on programs without needing the <em>prompt</em> to be specified at all:</p>
<ul>
<li class=""><strong>A sound type checker</strong> rejects programs that reference functions that don't exist, that pass arguments of the wrong shape, that mix incompatible types. That's a hallucination-detection system, caught statically, no test runs required.</li>
<li class=""><strong>Data-flow analysis</strong> catches uninitialized reads, use-after-free, leaked resources, and a long list of memory and ownership errors that no amount of LLM "review" will reliably find.</li>
<li class=""><strong>Abstract interpretation</strong> proves bounds on values — array indices, integer overflow, null dereference — across all execution paths, not just the ones a test happened to exercise.</li>
<li class=""><strong>Symbolic execution</strong> explores program paths a test suite would never reach, finding inputs that violate assertions.</li>
<li class=""><strong>Bounded model checking</strong> exhaustively explores program behaviors up to a finite bound. Counterexamples it finds are real bugs; the absence of one means any remaining bug has to be deeper than the bound you checked. This is a stronger guarantee than "we tested some inputs," and weaker than full verification.</li>
</ul>
<p>None of this is speculative. It is what has been shipping in industrial compilers and verifiers for decades — Coverity, Infer, Astrée, KLEE, CBMC, Frama-C, the seL4 verification effort. The agent era is not asking for new science. It is asking us to point existing science at a new producer of code.</p>
<p>The semantic ground that the prompt fails to establish gets re-established by analysis of the <em>output program</em>. The agent produces; the apparatus verifies; the program ships only if the apparatus signs off.</p>
<p>It's worth being precise about the topology here, because it's the place where the compiler analogy is genuinely loose. In the compiler world, verification is <em>intricated</em> in the production pipeline: the type checker runs before codegen, the optimizer is constrained to semantics-preserving transformations, the linker validates symbols. By the time you have an output, the apparatus has already done its work. In the agent world, the topology is looser. The agent can generate and ship straight to the apparatus as a downstream gate, or it can interleave — generate a fragment, run the type checker, take the errors, continue — with verification stepping in at whatever granularity the loop allows. Different topology, same principle: the trust lives in the pipeline, not in the artifact. The asymmetry that <em>does</em> remain is one of defaults: the compiler's verification is non-optional and built in, while the agent's has to be deliberately wired into the loop. That's an argument for tighter integration over time — not for adding verification to the loop, but for making it the default that the agent can't opt out of.</p>
<p>This is what we are building at SkipLabs. SKJS is a TypeScript-compatible type checker that is <em>sound</em> — it doesn't have the escape hatches that make standard TypeScript ergonomic for humans and unreliable as a verification layer. The reactive runtime enforces explicit dependency contracts that an agent has to satisfy before code runs. This is pedantry that would feel like friction to a human author and is exactly right for an agent that needs to be held accountable for what it generates <em>before</em> anything reaches production.</p>
<p>The apparatus exists. Some of it ships today. More of it is being ported from the compiler world into the agent world right now. The point of the original piece — and the point I should have made more clearly — is that this is where the engineering work is. Not in scaling code review. Not in waiting for better models. In building the static guarantees that make trust warranted.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Code Was Never for Machines — Until Now]]></title>
            <link>https://skiplabs.io/blog/future_of_tools_for_ai</link>
            <guid>https://skiplabs.io/blog/future_of_tools_for_ai</guid>
            <pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[We spent decades making languages readable. Agents don't care. Why we'll resist this shift — and why we'll embrace it anyway.]]></description>
            <content:encoded><![CDATA[<p>In my <a href="https://skiplabs.io/blog/codegen_as_compiler" target="_blank" rel="noopener noreferrer" class="">last post</a>, I argued that we should treat agent output the way we treat compiler output: not something to be read and reviewed by humans, but something to be verified by process. The framing resonated with a lot of people, but it leaves a deeper question on the table.</p>
<p>If agents are now the primary authors of code, should the tools and languages those agents use still be optimized for humans?</p>
<p>I don't think they should. And the history of programming languages tells us exactly why we'll resist that change — and why we'll embrace it anyway.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="programming-was-always-a-human-readability-project">Programming Was Always a Human-Readability Project<a href="https://skiplabs.io/blog/future_of_tools_for_ai#programming-was-always-a-human-readability-project" class="hash-link" aria-label="Direct link to Programming Was Always a Human-Readability Project" title="Direct link to Programming Was Always a Human-Readability Project" translate="no">​</a></h2>
<p>Every major leap forward in programming language design has been, at its core, a leap toward human legibility. Assembly gave way to C because humans could reason about it. C gave way to higher-level languages because abstraction made programs easier to hold in a person's head. And at the far end of that arc, you get Python and Ruby: languages whose defining virtue is that they read almost like English prose.</p>
<p>Python's indentation rules aren't a technical necessity — they're a readability convention enforced by the language itself. Ruby's syntax is deliberately soft, forgiving, close to natural language. The <a href="https://www.tiobe.com/tiobe-index/" target="_blank" rel="noopener noreferrer" class="">TIOBE index</a> (a widely cited ranking of programming language popularity) doesn't measure what's technically optimal. It measures what engineers want to write and read. For decades, those were the same thing.</p>
<p>The tools followed the same logic. Error messages were written for people. Outputs were formatted for terminal reading. Type systems were made optional or loosened to reduce friction for the human author. Everything was, and still is, optimized for the person sitting at the keyboard.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hack-and-the-last-time-we-had-this-argument">Hack and the Last Time We Had This Argument<a href="https://skiplabs.io/blog/future_of_tools_for_ai#hack-and-the-last-time-we-had-this-argument" class="hash-link" aria-label="Direct link to Hack and the Last Time We Had This Argument" title="Direct link to Hack and the Last Time We Had This Argument" translate="no">​</a></h2>
<p>We've been here before, though the stakes were smaller.</p>
<p>When Julien Verlaguet and the team at Facebook created Hack, they were solving a specific problem: PHP, the language powering the entire Facebook codebase, was fundamentally type-unsafe. At the scale Facebook operated, that wasn't just an aesthetic problem, it was an engineering liability. You couldn't refactor with confidence. You couldn't catch entire classes of bugs statically. The language was optimized for the speed of a solo developer writing web pages, not for the discipline of thousands of engineers maintaining hundreds of millions of lines.</p>
<p>So Julien built Hack: a gradually-typed, rigorously annotated replacement that was strictly less pleasant to write than PHP. It was more verbose. It demanded precision. It required you to say exactly what you meant. And then they got every engineer at Facebook to switch.</p>
<p>The rollout did not go smoothly at first. Engineers pushed back hard. They asked for escape hatches — ways to keep writing PHP in the parts of the codebase they owned. They requested exemptions, delayed migration timelines, special-case bypasses. Some wanted help porting their code, others simply wanted to be left alone. The friction was real and sustained.</p>
<p>But the org held the line. The resistance gave way not because engineers were eventually won over by argument, but because the environment made staying on PHP progressively harder. The CI pipeline was tightened incrementally — more checks, stricter gates, less tolerance for untyped code — and the hierarchy didn't offer the opt-outs people asked for. The path of least resistance slowly became compliance.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="agents-dont-need-the-training-wheels">Agents Don't Need the Training Wheels<a href="https://skiplabs.io/blog/future_of_tools_for_ai#agents-dont-need-the-training-wheels" class="hash-link" aria-label="Direct link to Agents Don't Need the Training Wheels" title="Direct link to Agents Don't Need the Training Wheels" translate="no">​</a></h2>
<p>A coding agent doesn't need readable syntax. It doesn't need forgiving type semantics. It doesn't need terse, human-friendly error messages. It needs the opposite.</p>
<p>An agent benefits from verbosity. Long, precise, unambiguous tool outputs are easier to parse than short, clever, human-optimized ones. An agent benefits from pedantic execution — systems that fail loudly and specifically when something is wrong, rather than silently coercing types or swallowing errors in the name of developer experience. An agent benefits from strong type systems not because it needs guardrails, but because types are the most information-dense way to describe what code is supposed to do.</p>
<p>When a human reads an error message, you want it to be concise, sympathetic, maybe even helpful in a narrative sense. When an agent reads an error message, you want it to be complete, structured, and machine-parseable. These are different optimization targets that have been collapsed into one because until recently, the reader was always human.</p>
<p>The same applies to tool APIs, CLI outputs, documentation formats, error codes. All of it was designed for a human consumer. That assumption is now in question.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-coming-divergence">The Coming Divergence<a href="https://skiplabs.io/blog/future_of_tools_for_ai#the-coming-divergence" class="hash-link" aria-label="Direct link to The Coming Divergence" title="Direct link to The Coming Divergence" translate="no">​</a></h2>
<p>I expect we'll see a bifurcation in tooling. On one side, languages and tools that continue to optimize for human authors — and there will always be human authors. On the other, a new generation of tools designed with the agent as the primary consumer: strict, verbose, formally specified, hostile to ambiguity.</p>
<p>This is what we're building toward at SkipLabs. SKJS, our TypeScript-compatible type checker, is sound in ways that standard TypeScript isn't. That makes it harder for humans and better for agents. Our reactive runtime enforces explicit dependency contracts that would feel over-engineered to a developer writing code by hand, and are exactly right for an agent that needs to reason about what depends on what.</p>
<p>The pattern is consistent: pedantry that felt like friction when humans were the authors becomes an asset when the author is an agent operating at scale.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-well-resist-this-and-why-well-do-it-anyway">Why We'll Resist This, and Why We'll Do It Anyway<a href="https://skiplabs.io/blog/future_of_tools_for_ai#why-well-resist-this-and-why-well-do-it-anyway" class="hash-link" aria-label="Direct link to Why We'll Resist This, and Why We'll Do It Anyway" title="Direct link to Why We'll Resist This, and Why We'll Do It Anyway" translate="no">​</a></h2>
<p>Julien's engineers resisted the switch to Hack. PHP felt natural. Hack felt bureaucratic. And they made the switch because the system-level argument was overwhelming.</p>
<p>We'll resist this transition for the same reason: the new tools will feel worse to write, and they will be worse to write, by human standards. That's not a design failure — it's a deliberate optimization for a different consumer. The discomfort is the point.</p>
<p>But the system-level argument is, again, overwhelming. If agents are generating the bulk of code, and the tools those agents use are designed around human readability, we're leaving enormous capability on the table. We're asking the agent to work in a medium optimized for someone else.</p>
<p>We made the tools more readable to get more developers. We'll make them less readable — more precise, more formal, more machine-native — to get better agents. The readability era of programming languages was long and productive, and is now coming to an end.</p>
<hr>
<p><em>This post follows <a href="https://skiplabs.io/blog/codegen_as_compiler" target="_blank" rel="noopener noreferrer" class="">Treat Agent Output Like Compiler Output</a>.</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Treat Agent Output Like Compiler Output]]></title>
            <link>https://skiplabs.io/blog/codegen_as_compiler</link>
            <guid>https://skiplabs.io/blog/codegen_as_compiler</guid>
            <pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Why our discomfort with AI-generated code reveals exactly what we haven't built yet, and what the compiler analogy teaches us about trusting coding agents.]]></description>
            <content:encoded><![CDATA[<p>Philip Su's recent post argues that code reviews are not just impractical in the age of coding agents, they're headed toward being <em>irresponsible</em>. He's right on trend. But I think the framing of "lights-out codebases" skips over the more interesting and uncomfortable question: <strong>why does lights-out feel so scary</strong>, and what does that fear actually tell us?</p>
<p>The answer, I think, is hiding in something we already used once before and then promptly forgot we did: the compiler.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="nobody-reviews-compiler-output">Nobody Reviews Compiler Output<a href="https://skiplabs.io/blog/codegen_as_compiler#nobody-reviews-compiler-output" class="hash-link" aria-label="Direct link to Nobody Reviews Compiler Output" title="Direct link to Nobody Reviews Compiler Output" translate="no">​</a></h2>
<p>Think about how you relate to your compiler. You write C++, Rust, Go, and the toolchain spits out a binary. Do you open that binary and read through the assembly? Do you schedule a meeting with a colleague to review the object code before shipping?</p>
<p>Of course not. That would be absurd. And not because you blindly trust compilers, you don't. Compilers have bugs. Compilers have had famously catastrophic bugs. But you've constructed an entire apparatus that makes reviewing the output <em>unnecessary</em>: you write tests against observable behavior, you have type systems that constrain what the output can do, you have reproducible builds, you have fuzzing and sanitizers and formal verification in high-stakes domains. You trust the <em>process</em>, not the artifact.</p>
<p>We haven't built that apparatus for coding agents. And that, not the output itself, is what's actually missing.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-real-diagnosis">The Real Diagnosis<a href="https://skiplabs.io/blog/codegen_as_compiler#the-real-diagnosis" class="hash-link" aria-label="Direct link to The Real Diagnosis" title="Direct link to The Real Diagnosis" translate="no">​</a></h2>
<p>Consider that a developer like Michael Novati landed 417 PRs in a single day in February. That's enough to argue that reviewing AI-generated code is volumetrically impossible. And it is. But I'd push the diagnosis further: <strong>the volume isn't the problem, it's a symptom that exposes the problem</strong>.</p>
<p>The problem is that we're still treating agent output the way we used to treat junior developer output, as something that needs a human to eyeball it before it's real. That made sense when code reviews were the primary quality gate. It makes no sense when they can't be.</p>
<p>The compiler analogy is clarifying here not because it tells us to trust agents blindly, but because it shows us what a mature pipeline looks like once you stop treating artifacts as things to be <em>read</em> and start treating them as things to be <em>verified</em>. We don't review compiled binaries. We run them against test suites, we check them against specifications, we instrument them in production. We moved the quality control upstream (type systems, linters, formal specs) and downstream (testing, monitoring, rollback), and eliminated the manual middle.</p>
<p>That's exactly the move we need to make with agents. We're not there yet. And the reason the lights-out framing makes engineers nervous isn't irrationality, it's that the upstream and downstream apparatus barely exists.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-actually-missing">What's Actually Missing<a href="https://skiplabs.io/blog/codegen_as_compiler#whats-actually-missing" class="hash-link" aria-label="Direct link to What's Actually Missing" title="Direct link to What's Actually Missing" translate="no">​</a></h2>
<p>If compiler output requires no human review because of what surrounds it, then agent output will require no human review when we've built the equivalent. What does that actually mean?</p>
<p><strong>Upstream:</strong> Compilers have type systems, formal contracts about what code <em>means</em> before it runs. For agents, this equivalent is still primitive. We have prompts, we have some scaffolding, but we don't yet have robust formal specifications that an agent is verifiably executing against. The closest analogs, TDD, contract testing, design-by-specification, exist but are not yet standard practice when <em>agents</em> are the author.</p>
<p><strong>The verification layer:</strong> Compilers are deterministic. Given the same source, you get the same output. Agents are not. This means the test suite that used to catch <em>human</em> mistakes needs to be substantially more comprehensive when the author can produce plausible-but-subtly-wrong code at 50x the rate. The idea of using AI to check AI, pre- and post-action reviews, dedicated security agents, is the right direction. But it's nascent, and we're nowhere near treating it as infrastructure.</p>
<p><strong>Downstream:</strong> The production monitoring and rollback culture that makes deploying compiled binaries safe is reasonably mature. Canary deploys, feature flags, observability tooling, this stuff works. Applying it rigorously to agent-generated changes is tractable. It just isn't yet habitual.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-uncomfortable-part">The Uncomfortable Part<a href="https://skiplabs.io/blog/codegen_as_compiler#the-uncomfortable-part" class="hash-link" aria-label="Direct link to The Uncomfortable Part" title="Direct link to The Uncomfortable Part" translate="no">​</a></h2>
<p>Here's the contrarian edge: the engineers most resistant to lights-out codebases are often the same engineers who would resist, if they could time-travel back, the idea that you don't need to review your linker's output. The intuition feels protective. It is actually just unfamiliarity with where the trust has been relocated.</p>
<p>Trusting compiler output isn't naïve. It's the result of decades of investment in making that trust warranted. We didn't skip the hard work, we did the hard work in a different place. The same logic applies here. The question isn't "should we trust agent output?" It's "have we built the infrastructure that makes that trust reasonable?"</p>
<p>Right now, mostly, we haven't. That's not an argument against moving toward lights-out codebases, we're being pushed there regardless. It's an argument for being honest about what "moving there responsibly" actually requires.</p>
<p>Hardware chip companies already work with black-box components verified by acceptance tests rather than human review. That's the model. But notice: chip verification is a <em>discipline</em>, with tooling, with formal methods, with teams of people whose entire job is designing the test harness. We are going to need the software equivalent, and it is probably going to be harder, because software has far less formal specification culture than hardware.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-to-build">What To Build<a href="https://skiplabs.io/blog/codegen_as_compiler#what-to-build" class="hash-link" aria-label="Direct link to What To Build" title="Direct link to What To Build" translate="no">​</a></h2>
<p>The lights-out codebase is coming. Su is right about that. But rather than simply accepting it as an inevitability to brace for, we should treat it as a <em>design target</em>, one that tells us exactly what we need to build:</p>
<ul>
<li class="">Formal specification layers that agents execute against, not just prompts</li>
<li class="">Test infrastructure robust enough to substitute for the code comprehension that reviews provide</li>
<li class="">AI-checks-AI pipelines as first-class CI infrastructure, not bolt-on curiosity</li>
<li class="">Production instrumentation good enough that bad agent behavior is caught fast and rolled back faster</li>
</ul>
<p>The compiler didn't make us stop caring about correctness. It moved where correctness is enforced. That's the project in front of us. The scary part isn't the lights-out codebase. The scary part is how few teams are treating what replaces the review as serious engineering work.</p>
<p><em>This post was written in response to Philip Su's "<a href="https://molochinations.substack.com/p/no-more-code-reviews-lights-out-codebases" target="_blank" rel="noopener noreferrer" class="">No More Code Reviews: Lights-Out Codebases Ahead</a>"</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Discussion with Lucas, a SkipLabs engineer]]></title>
            <link>https://skiplabs.io/blog/interview_lucas</link>
            <guid>https://skiplabs.io/blog/interview_lucas</guid>
            <pubDate>Fri, 11 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[An in-depth conversation with a SkipLabs engineer about reactive programming, performance optimization, and the technical challenges of building scalable data synchronization tools.]]></description>
            <content:encoded><![CDATA[<p>Ever wondered what it's like to work on cutting-edge reactive programming? This chat with a Lucas, a SkipLabs engineer, gives you the inside scoop on building tools that tackle gnarly data synchronization problems. We have created something called the Skip Framework, which runs on our own programming language, SkipLang, pretty cool stuff. What makes this interview particularly interesting is how it bounces between the nuts and bolts of performance testing (like figuring out why stream creation was too slow) and the bigger picture of where tech is headed. You'll get a feel for SkipLabs' laid-back engineering culture where people work independently but always have each other's backs, plus some thoughtful takes on everything from open source development to why we still edit code like it's 1995. Whether you're into the technical weeds of load testing or curious about how small teams tackle ambitious projects, there's something here for you.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-long-have-you-been-working-at-skiplabs">How long have you been working at SkipLabs?<a href="https://skiplabs.io/blog/interview_lucas#how-long-have-you-been-working-at-skiplabs" class="hash-link" aria-label="Direct link to How long have you been working at SkipLabs?" title="Direct link to How long have you been working at SkipLabs?" translate="no">​</a></h2>
<p>I joined SkipLabs in November 2022, so it's been a little over two and a half years now.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="in-a-few-words-what-do-you-do-at-skiplabs">In a few words, what do you do at SkipLabs?<a href="https://skiplabs.io/blog/interview_lucas#in-a-few-words-what-do-you-do-at-skiplabs" class="hash-link" aria-label="Direct link to In a few words, what do you do at SkipLabs?" title="Direct link to In a few words, what do you do at SkipLabs?" translate="no">​</a></h2>
<p>At SkipLabs, we develop tools focused on reactive programming. Our goal is to solve complex data synchronization problems while making this approach accessible and cost-effective in terms of infrastructure.</p>
<p>In practical terms, reactive programming is a declarative way of expressing computations: instead of manually handling state transitions, you simply describe state as a function of multiple inputs.</p>
<p>To make this approach tangible and usable on a daily basis, we created the Skip Framework—a reactive programming framework designed to be both powerful and easy to adopt.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-would-you-describe-the-engineering-culture-at-skiplabs">How would you describe the engineering culture at SkipLabs?<a href="https://skiplabs.io/blog/interview_lucas#how-would-you-describe-the-engineering-culture-at-skiplabs" class="hash-link" aria-label="Direct link to How would you describe the engineering culture at SkipLabs?" title="Direct link to How would you describe the engineering culture at SkipLabs?" translate="no">​</a></h2>
<p>I’d say the engineering culture at SkipLabs is built first and foremost on trust and individual responsibility. Everyone pulls their weight and doesn’t hesitate to get their hands dirty when needed—even with tasks that may be a bit thankless. There’s a real collective drive to move things forward, and it shows in the general vibe: everyone is experienced, which allows us to work in parallel in a smooth and efficient way.</p>
<p>We operate in a fairly open and independent manner, with very few meetings. And despite this autonomy, there’s a lot of mutual support—whenever a question arises, someone is always there to help. Our expertise is quite diverse, which is a real strength. Several people on the team have a strong background in programming languages and a deep interest in low-level systems—especially compilers and LLVM, which sit at the heart of our stack.</p>
<p>On the tech side, the Skip Framework is currently exposed via a TypeScript API, and soon also via a native API in SkipLang. At the core of it all is a programming language, SkipLang, designed by Julien (previously at Facebook). The language was built to provide strong guarantees about data mutability—making it clear what can and cannot change, to enable safe and efficient cache invalidation.
We use this language today. It has its own compiler, written in SkipLang itself, which targets LLVM as a backend. This gives us strong performance while maintaining high portability: the framework can be compiled natively or to WebAssembly, which makes it possible, for example, to run it directly in a browser. In addition to SkipLang and TypeScript, we also use some Python and Bash—classic tools to keep everything running smoothly.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="so-how-do-you-approach-scalability-challenges">So how do you approach scalability challenges?<a href="https://skiplabs.io/blog/interview_lucas#so-how-do-you-approach-scalability-challenges" class="hash-link" aria-label="Direct link to So how do you approach scalability challenges?" title="Direct link to So how do you approach scalability challenges?" translate="no">​</a></h2>
<p>For me, scalability starts with the ability to accurately measure performance. Before even thinking about optimization, you need reliable metrics—and ideally, with minimal manual intervention. Manual handling quickly becomes a source of errors, especially in systems as complex as ours.</p>
<p>It’s crucial to measure what you actually want to observe—not just loosely correlated metrics. Once you have a solid measurement foundation, you can start large-scale analysis, deep profiling, pinpoint real bottlenecks, and invest where it really matters. Right now, we’re specifically working on load testing Skip to better understand how our system behaves under different loads, and to validate our horizontal scalability. It’s a progressive process, but essential to ensuring strong performance in varied environments.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="and-in-your-latest-tests-what-were-the-main-bottlenecks-you-identified">And in your latest tests, what were the main bottlenecks you identified?<a href="https://skiplabs.io/blog/interview_lucas#and-in-your-latest-tests-what-were-the-main-bottlenecks-you-identified" class="hash-link" aria-label="Direct link to And in your latest tests, what were the main bottlenecks you identified?" title="Direct link to And in your latest tests, what were the main bottlenecks you identified?" translate="no">​</a></h2>
<p>One of the first bottlenecks we found was in the creation of streams. Once a stream is set up, performance is excellent, even with multiple users subscribing in parallel. But the initial creation phase can be a bit too slow. We’re currently working on a fix to improve that.</p>
<p>This kind of issue really shows how important fine-grained, automated tracing is—to avoid local changes negatively affecting other parts of the system. Working as a team also means writing code that helps—or at least doesn’t hinder—others’ work. This is why we’re implementing recurring load tests that run automatically in our CI. The goal is to detect performance regressions early and make sure different optimizations continue to work well together, even as the codebase evolves.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="and-in-those-load-tests-what-metrics-do-you-prioritize">And in those load tests, what metrics do you prioritize?<a href="https://skiplabs.io/blog/interview_lucas#and-in-those-load-tests-what-metrics-do-you-prioritize" class="hash-link" aria-label="Direct link to And in those load tests, what metrics do you prioritize?" title="Direct link to And in those load tests, what metrics do you prioritize?" translate="no">​</a></h2>
<p>There are several, but the most important is: how many users can we serve under acceptable conditions for a given budget? We aim to stay close to real-world use cases. Depending on the services being tested, some loads involve heavy writing, others are mostly read-heavy. Some may involve lots of shared data, others significant server-side computation before results are pushed out.</p>
<p>One key metric is replication time: when a client writes data, how long does it take for that data to be visible to all other affected clients? This delay depends on the associated computation graph, so it can vary a lot—but it’s an essential metric for us.
Once we’ve defined what counts as an acceptable client-side response time, we check how many clients we can serve with a single instance. Then we measure how that capacity scales when adding more servers—that’s our horizontal scalability curve.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="and-on-the-server-side-what-do-you-measure">And on the server side, what do you measure?<a href="https://skiplabs.io/blog/interview_lucas#and-on-the-server-side-what-do-you-measure" class="hash-link" aria-label="Direct link to And on the server side, what do you measure?" title="Direct link to And on the server side, what do you measure?" translate="no">​</a></h2>
<p>There, we focus on how well we’re using available resources. For example: how many server instances can we run efficiently on a given machine? Are we fully utilizing RAM, CPU cores? There’s no point in paying for unused capacity. We complement that with regular profiling to ensure there are no avoidable bottlenecks in our server stack. It’s an ongoing effort, but it gives us real visibility into the system’s actual performance.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="beyond-technical-challenges-what-do-you-see-as-the-bigger-issues-in-this-field">Beyond technical challenges, what do you see as the bigger issues in this field?<a href="https://skiplabs.io/blog/interview_lucas#beyond-technical-challenges-what-do-you-see-as-the-bigger-issues-in-this-field" class="hash-link" aria-label="Direct link to Beyond technical challenges, what do you see as the bigger issues in this field?" title="Direct link to Beyond technical challenges, what do you see as the bigger issues in this field?" translate="no">​</a></h2>
<p>I see three fundamental challenges:</p>
<ul>
<li class="">
<p><strong>Correctness</strong> – We want clients to receive data that is both fresh and accurate, with mathematical guarantees that what they see is consistent with the system’s state. That’s non-negotiable.</p>
</li>
<li class="">
<p><strong>Performance</strong> – All of that has to happen as fast as possible, even at large scale. Offering low-latency reactivity is a bold promise—and a serious technical challenge.</p>
</li>
<li class="">
<p><strong>Developer ergonomics</strong> – This is often underestimated but just as crucial. Our goal is for developers to express business logic as declaratively and clearly as possible. Ideally, they focus only on what they want to produce, not how data will sync or update. It’s our job to ensure that works smoothly behind the scenes.</p>
</li>
</ul>
<p>To achieve this, we need a sound and efficient computation model, solid implementation, and a clean codebase. That’s what we’re building with Skip: a technical foundation that lets you offload complexity without compromising rigor.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-do-you-enjoy-most-about-your-work-right-now">What do you enjoy most about your work right now?<a href="https://skiplabs.io/blog/interview_lucas#what-do-you-enjoy-most-about-your-work-right-now" class="hash-link" aria-label="Direct link to What do you enjoy most about your work right now?" title="Direct link to What do you enjoy most about your work right now?" translate="no">​</a></h2>
<p>Benchmarking itself isn’t necessarily what excites me most—but right now it’s pretty thrilling, because we’re entering a new phase of the project. After months of prototyping and refining the core of the framework, we’re finally putting it through real-world load testing. Now we can truly measure how far this new paradigm can go under real conditions. It’s gratifying to see that what we’ve built—brick by brick—is starting to hold up at scale, and that the choices we’ve made are beginning to pay off.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="taking-a-step-back-what-would-you-like-to-see-evolve-in-computing-today">Taking a step back, what would you like to see evolve in computing today?<a href="https://skiplabs.io/blog/interview_lucas#taking-a-step-back-what-would-you-like-to-see-evolve-in-computing-today" class="hash-link" aria-label="Direct link to Taking a step back, what would you like to see evolve in computing today?" title="Direct link to Taking a step back, what would you like to see evolve in computing today?" translate="no">​</a></h2>
<p>There are quite a few things I’d like to see change, but if I had to summarize, I’d highlight three key areas that matter most to me:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="valuing-open-source-as-a-shared-public-good">Valuing open source as a shared public good:<a href="https://skiplabs.io/blog/interview_lucas#valuing-open-source-as-a-shared-public-good" class="hash-link" aria-label="Direct link to Valuing open source as a shared public good:" title="Direct link to Valuing open source as a shared public good:" translate="no">​</a></h3>
<p>I believe open source—or more broadly, digital commons—are still vastly underdeveloped relative to their importance. These are software foundations that benefit everyone and deserve more collective investment.</p>
<p>At SkipLabs, we work in open source under MIT license, and that’s one of the reasons I joined the team.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="rethinking-how-we-interact-with-code">Rethinking how we interact with code:<a href="https://skiplabs.io/blog/interview_lucas#rethinking-how-we-interact-with-code" class="hash-link" aria-label="Direct link to Rethinking how we interact with code:" title="Direct link to Rethinking how we interact with code:" translate="no">​</a></h3>
<p>Today, as developers, our interaction with code is still quite “primitive”: we’re just editing letters on a page. Even though modern editors add powerful features, it’s still largely syntax-level work.
I’d like us to move toward tools that support semantic interaction with code—like version control that understands the intent behind a change, not just a diff of characters. That would unlock deeper collaboration, better understanding, and smarter analysis tools. It’s a major area for innovation.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="mainstreaming-cryptography-in-the-digital-public-sphere">Mainstreaming cryptography in the digital public sphere:<a href="https://skiplabs.io/blog/interview_lucas#mainstreaming-cryptography-in-the-digital-public-sphere" class="hash-link" aria-label="Direct link to Mainstreaming cryptography in the digital public sphere:" title="Direct link to Mainstreaming cryptography in the digital public sphere:" translate="no">​</a></h3>
<p>I think we should make broader use of asymmetric cryptography for things involving public speech, traceability, or authorship—without necessarily resorting to blockchain. Digital signatures alone are enough in many cases.</p>
<p>Right now, these tools are still largely inaccessible to the general public, which is a real problem. I think it’s crucial to defend a web architecture that respects individual freedoms—and that includes broad adoption of tools like encrypted email, and digital signatures, ideally within an open-source ecosystem.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="thanks-lucas">Thanks, Lucas<a href="https://skiplabs.io/blog/interview_lucas#thanks-lucas" class="hash-link" aria-label="Direct link to Thanks, Lucas" title="Direct link to Thanks, Lucas" translate="no">​</a></h2>
<p>Thanks to you !</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cache Invalidation and Reactive Systems]]></title>
            <link>https://skiplabs.io/blog/cache_invalidation</link>
            <guid>https://skiplabs.io/blog/cache_invalidation</guid>
            <pubDate>Fri, 04 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A technical blog post examining cache invalidation challenges in distributed systems and presenting SkipLabs' reactive backend approach as an alternative to traditional strategies.]]></description>
            <content:encoded><![CDATA[<p>Phil Karlton famously said <em>there are only two hard things in computer science: cache invalidation and naming things</em>. While naming things may be subjective, <a href="https://en.wikipedia.org/wiki/Cache_invalidation" target="_blank" rel="noopener noreferrer" class="">cache invalidation</a> is a problem that only gets harder as our systems grow more complex. As applications scale and users expect real-time data everywhere, traditional approaches to cache management crack under pressure.</p>
<p>Here's the thing about cache invalidation: it sits right at the center of the classic performance versus consistency trade-off. Mess it up, and your users are stuck looking at stale data. Maybe they see yesterday's stock prices, or worse, they're making business decisions based on outdated metrics. But nail the consistency part while implementing it poorly, and you've just thrown away all the performance benefits that made you add caching in the first place. This fundamental tension has been driving innovation in caching strategies for decades, from simple <a href="https://en.wikipedia.org/wiki/Time_to_live" target="_blank" rel="noopener noreferrer" class="">Time-to-live (TTL)</a> expiration to increasingly complex dependency tracking systems.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-infrastructure-caching-challenge">The Infrastructure Caching Challenge<a href="https://skiplabs.io/blog/cache_invalidation#the-infrastructure-caching-challenge" class="hash-link" aria-label="Direct link to The Infrastructure Caching Challenge" title="Direct link to The Infrastructure Caching Challenge" translate="no">​</a></h2>
<p>Modern applications cache everywhere, from <a href="https://en.wikipedia.org/wiki/CPU_cache" target="_blank" rel="noopener noreferrer" class="">CPU L1/L2 caches</a> to <a href="https://en.wikipedia.org/wiki/Content_delivery_network" target="_blank" rel="noopener noreferrer" class="">CDN edge caches</a> to application-level query caches, and each layer brings its own invalidation headaches that get exponentially worse across distributed systems. You've probably dealt with the classic cache invalidation strategies, and you know they all suck in their own special ways: TTL forces you to choose between stale data and cache misses, manual invalidation becomes impossible to maintain as dependencies grow, and event-driven invalidation couples your business logic to caching infrastructure.</p>
<p><img decoding="async" loading="lazy" alt="TTL Caching: When Stale Data Breaks User Experience" src="https://skiplabs.io/assets/images/ttl_cache-b1669760f282c79999fecb0cfb4f9bea.png" width="2136" height="812" class="img_ev3q"></p>
<p>Then there's the distributed systems reality check. <a href="https://en.wikipedia.org/wiki/Network_partition" target="_blank" rel="noopener noreferrer" class="">Network partitions</a> happen, clocks drift, and eventual consistency models mean that even your best-designed invalidation strategies can fail spectacularly. Your invalidation message gets delayed, lost, or arrives out of order, and suddenly half your cache nodes are serving stale data while the other half got the memo. These edge cases are the worst: they're nearly impossible to test properly and usually only surface when you're already under production load.</p>
<p>If you're running database-backed applications, you're dealing with multiple cache layers between your data store and your users. A single database update might need to invalidate entries in <a href="https://en.wikipedia.org/wiki/Redis" target="_blank" rel="noopener noreferrer" class="">Redis</a>, <a href="https://en.wikipedia.org/wiki/Memcached" target="_blank" rel="noopener noreferrer" class="">Memcached</a>, your application's memory cache, and your CDN caches. Coordinating this invalidation across all these different systems while maintaining performance and consistency? That's the kind of engineering challenge that makes you question your career choices.</p>
<p>Ok, I may be a little dramatic here, but now that we agree on the problem we're talking about...</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="skiplabs-a-reactive-approach-to-cache-invalidation">SkipLabs: A Reactive Approach to Cache Invalidation<a href="https://skiplabs.io/blog/cache_invalidation#skiplabs-a-reactive-approach-to-cache-invalidation" class="hash-link" aria-label="Direct link to SkipLabs: A Reactive Approach to Cache Invalidation" title="Direct link to SkipLabs: A Reactive Approach to Cache Invalidation" translate="no">​</a></h2>
<p>At <a href="https://skiplabs.io/" target="_blank" rel="noopener noreferrer" class="">SkipLabs</a>, we take a different approach to the cache invalidation problem through reactive backend architecture. Instead of treating caching as a separate concern requiring manual coordination, SkipLabs builds reactivity right into the core of your data processing pipeline. When your underlying data changes, all dependent computations and cached results automatically update in a cascading fashion.</p>
<p>The reactive model that SkipLabs uses draws from functional reactive programming and incremental computation research. When data changes, the system automatically figures out which computations depend on that data and updates them incrementally. You don't need manual cache invalidation because the <em>cache</em> is always in sync with the underlying data through the reactive dependency graph.</p>
<p><img decoding="async" loading="lazy" alt="Automatic Cache Updates with SkipLabs" src="https://skiplabs.io/assets/images/reactive_cache_skip-27193421d695423539ab2185092e0a60.png" width="2214" height="1526" class="img_ev3q"></p>
<p>SkipLabs pulls this off through a sophisticated runtime that tracks fine-grained dependencies between data and computations. Unlike traditional caching systems where invalidation is something you bolt on afterward, the SkipLabs runtime makes invalidation an intrinsic part of how computation works. When data changes, the runtime precisely identifies which cached computations are affected and updates them incrementally.</p>
<p>The platform handles the complex scenarios that make traditional caching systems cry: joins across multiple data sources, aggregations over massive datasets, deeply nested dependency chains. By modeling these relationships explicitly, SkipLabs can provide strong consistency guarantees while keeping the performance benefits of caching.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pros-and-cons-of-the-reactive-approach">Pros and Cons of the Reactive Approach<a href="https://skiplabs.io/blog/cache_invalidation#pros-and-cons-of-the-reactive-approach" class="hash-link" aria-label="Direct link to Pros and Cons of the Reactive Approach" title="Direct link to Pros and Cons of the Reactive Approach" translate="no">​</a></h2>
<p>The reactive approach has some compelling advantages. First, you can finally stop writing manual invalidation logic. No more tracking complex dependency relationships or trying to remember which cache keys to invalidate when data changes. Your codebase becomes more maintainable, and you'll have fewer bugs related to stale data keeping you up at night.</p>
<p>Performance can be significantly better than traditional caching in many scenarios. Because the system understands your dependency graph, it can perform minimal incremental updates rather than nuking entire cache regions. For applications with complex data relationships, this means much more efficient cache utilization and faster response times when data changes.</p>
<p>The consistency guarantees are typically stronger than what you get with eventually consistent caching systems. Updates propagate automatically through the dependency graph, so there's less risk of serving stale data or having different parts of your application showing inconsistent views.</p>
<p>But let's be real about the trade-offs. The complexity doesn't disappear, it just moves from your application code into the runtime system. This can make debugging and performance tuning more challenging when things go wrong. You need to understand the reactive model and how your code will execute within it, which represents a significant learning curve for you and your team.</p>
<p>Converting existing code to the Skip runtime without properly structuring and decomposing its dependency structure can incur more resource overhead than it saves through incrementality. The benefits of reactive systems shine when computations are well-structured for incremental updates, but poorly designed dependency graphs can actually hurt performance compared to simpler caching strategies.</p>
<p>The reactive model also puts constraints on how you can structure your applications and access data. Not every existing codebase can be easily adapted to work within a reactive framework, you might need significant architectural changes to realize the benefits. To me, this is the hardest problem to solve.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrap-up">Wrap-up<a href="https://skiplabs.io/blog/cache_invalidation#wrap-up" class="hash-link" aria-label="Direct link to Wrap-up" title="Direct link to Wrap-up" translate="no">​</a></h2>
<p>Cache invalidation continues to be one of the most challenging problems in software engineering, with traditional approaches forcing difficult trade-offs between performance, consistency, and complexity. The reactive approach that platforms like SkipLabs are pioneering offers a promising alternative that could fundamentally change how we think about caching and data consistency.</p>
<p>Reactive systems aren't a silver bullet, they come with their own complexities and trade-offs. But they represent an important evolution in how we build data-intensive applications. As the technology matures and developer tooling improves, reactive backends may become viable for a much broader range of applications.</p>
<p>The key insight is that cache invalidation isn't just a technical problem to solve, it's a fundamental design challenge that shapes how we architect our systems. Whether you're using traditional invalidation strategies or exploring reactive approaches like SkipLabs, the goal remains the same: giving users fast, consistent access to data while maintaining system scalability and developer sanity. The reactive approach gives us a new tool for tackling this enduring challenge in computer science.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's Next?<a href="https://skiplabs.io/blog/cache_invalidation#whats-next" class="hash-link" aria-label="Direct link to What's Next?" title="Direct link to What's Next?" translate="no">​</a></h2>
<p>I'd love to hear what aspects of cache invalidation and reactive systems you'd like me to dive deeper into. Are you interested in:</p>
<ul>
<li class="">Detailed performance results for a reactive approach?</li>
<li class="">Deep technical implementation details of how SkipLabs tracks dependencies?</li>
<li class="">Real-world case studies of teams migrating from traditional caching to reactive systems?</li>
<li class="">Practical migration strategies for existing codebases?</li>
<li class="">Other distributed systems challenges that reactive architectures could solve?</li>
</ul>
<p>Drop me a line and let me know what you'd find most valuable, your feedback helps shape what I write about next.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Backend Pressure from a Reactive Point of View]]></title>
            <link>https://skiplabs.io/blog/backend_pressure</link>
            <guid>https://skiplabs.io/blog/backend_pressure</guid>
            <pubDate>Fri, 20 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A technical exploration of how reactive systems can solve backend pressure through persistent computational structures, introducing Skip as a framework that automates the complexity of building such systems.]]></description>
            <content:encoded><![CDATA[<p>Backend systems face constant pressure from multiple directions: user requests demanding instant responses, databases struggling under query loads, and servers managing finite computational resources. This backend pressure—the cumulative stress of handling concurrent requests while maintaining performance and resource constraints—manifests in bottlenecks, latency spikes, and system instability. Traditional approaches often treat these pressures as isolated problems—adding a cache here, optimizing a query there—resulting in complex patches that fix symptoms but ignore deeper architectural issues.</p>
<p>The conventional solution? Event-driven architectures and streaming systems. But here's the problem: these approaches force developers to manually create and manage streams, handle async complexity, deal with event schemas, and debug distributed streaming pipelines. You end up spending more time on streaming infrastructure than on your actual business logic.</p>
<p><img decoding="async" loading="lazy" alt="Traditional vs Reactive backend" src="https://skiplabs.io/assets/images/backend_pressure_illustration-6a2147bab88f009d7c3608700225f2a7.png" width="2100" height="1077" class="img_ev3q"></p>
<p>Think of a financial portfolio app: instead of calculating portfolio performance each time a user loads their dashboard, the server maintains continuous streams where position changes flow through pricing calculations, which flow through performance metrics, which flow through sector aggregations. Or an e-commerce site where product price changes automatically update product views, category aggregations, search indices, and recommendation scores in real-time. Rather than computing results on demand, reactive systems keep computational pipelines running continuously, so the answers are always up-to-date and ready for immediate use—reducing latency and spreading load over time instead of spiking under demand.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="precomputed-reactive-structures">Precomputed Reactive Structures<a href="https://skiplabs.io/blog/backend_pressure#precomputed-reactive-structures" class="hash-link" aria-label="Direct link to Precomputed Reactive Structures" title="Direct link to Precomputed Reactive Structures" translate="no">​</a></h2>
<p>A reactive approach maintains continuous streams: position changes flow through pricing calculations, which flow through performance metrics, which flow through sector aggregations. The portfolio view stays current automatically, and user requests return instantly regardless of portfolio complexity.</p>
<p>And here's the key insight that changes everything: we should be able to treat complex computations as persistent data structures rather than transient operations. These structures exist in memory, stay synchronized with source data, and serve requests without additional computation. This is the fundamental principle behind Skip's approach, which we'll explore in detail later.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cache-invalidation-as-dependency-management">Cache Invalidation as Dependency Management<a href="https://skiplabs.io/blog/backend_pressure#cache-invalidation-as-dependency-management" class="hash-link" aria-label="Direct link to Cache Invalidation as Dependency Management" title="Direct link to Cache Invalidation as Dependency Management" translate="no">​</a></h2>
<p>Traditional caching introduces the notorious cache invalidation problem: when source data changes, systems must remember to invalidate all dependent cached values. This leads to either stale data or overly aggressive invalidation that negates caching benefits.</p>
<p>Reactive systems eliminate manual cache invalidation by treating cached values as materialized views with explicit dependencies. When source data changes, the system automatically pushes updates through the dependency graph to wherever they are needed—whether that's materialized views, API endpoints, or system edges. Cache invalidation becomes dependency propagation.</p>
<p>For example, when a product price changes in an e-commerce system, the reactive pipeline automatically updates product views, category aggregations, search indices, and recommendation scores. Hand-written reactive systems typically handle simple event propagation but require a lot of extra code written by developers in the backend to manage complex stateful views and aggregations. Skip enables this ideal: the developer declares dependencies once; the system maintains consistency thereafter through automatic dependency management of complex computational states.</p>
<p>How can Skip enable this ideal? Three words for you: granular incremental updates.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="granular-incremental-updates">Granular Incremental Updates<a href="https://skiplabs.io/blog/backend_pressure#granular-incremental-updates" class="hash-link" aria-label="Direct link to Granular Incremental Updates" title="Direct link to Granular Incremental Updates" translate="no">​</a></h2>
<p>Skip reactive backends can perform granular, incremental updates rather than full recalculation when source data changes. If one product among thousands changes price, only the affected portions of aggregated views require updates. Category totals recalculate only for the affected category. Search indices update only relevant entries.</p>
<p>This granularity reduces computational overhead and memory churn. Systems can maintain complex materialized views that would be prohibitively expensive to recalculate entirely on each change.</p>
<p>Granular updates also improve system responsiveness. Instead of blocking while recalculating entire datasets, systems can process changes incrementally and continue serving requests throughout the update process.</p>
<p>I know what you're thinking: "This sounds great, but what's the catch?" Well, like most things in engineering, there are trade-offs to consider.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="memory-computation-trade-offs">Memory-Computation Trade-offs<a href="https://skiplabs.io/blog/backend_pressure#memory-computation-trade-offs" class="hash-link" aria-label="Direct link to Memory-Computation Trade-offs" title="Direct link to Memory-Computation Trade-offs" translate="no">​</a></h2>
<p>We make a trade-off: memory for computation time and request latency. Maintaining precomputed structures requires more memory than computing results on demand. However, memory costs have decreased significantly relative to computation costs and user experience requirements.</p>
<p>The trade-off becomes particularly favorable for read-heavy workloads where the same complex computations serve multiple requests. Computing once and serving many times provides better resource utilization than recomputing identical results repeatedly.
So we've covered the theory and the benefits, but what about the practical side? Well, implementing this isn't without its challenges.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementation-considerations">Implementation Considerations<a href="https://skiplabs.io/blog/backend_pressure#implementation-considerations" class="hash-link" aria-label="Direct link to Implementation Considerations" title="Direct link to Implementation Considerations" translate="no">​</a></h2>
<p>Implementing reactive backends requires careful consideration of data flow patterns and dependency graphs. Systems must handle backpressure when computation cannot keep pace with data changes. They must also manage memory usage as materialized views grow with data volume.</p>
<p>Monitoring remains important for reactive systems, though the metrics of interest shift from traditional patterns. Traditional metrics like request latency remain relevant, but reactive systems require additional visibility into stream processing rates, backpressure events, and materialized view sizes to understand system health.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="skips-approach-to-reactive-systems">Skip's Approach to Reactive Systems<a href="https://skiplabs.io/blog/backend_pressure#skips-approach-to-reactive-systems" class="hash-link" aria-label="Direct link to Skip's Approach to Reactive Systems" title="Direct link to Skip's Approach to Reactive Systems" translate="no">​</a></h2>
<p>What if there was a completely different way to tackle this complexity? What if we could get all the benefits of reactive systems without having to build or manage the streams ourselves? This is where Skip comes from.</p>
<p>Skip is a reactive framework that automatically creates and manages streams for you based on declarative computations. You write code as if time is frozen—defining what your outputs should look like based on your inputs at a single point in time—and Skip automatically turns this into live reactive streams that update clients in real-time. No manual stream wiring, no event handling complexity, no dependency tracking. Just declarative logic that automatically becomes reactive infrastructure.</p>
<p>Think of it this way: instead of programming reactive streams directly, you write functions that describe relationships between data. Skip automatically generates the optimal reactive streams to keep everything synchronized and delivers updates to clients seamlessly.</p>
<p>This approach fundamentally changes how we think about backend pressure because you're no longer computing results on demand or managing complex event flows. Skip handles the reactive infrastructure while you focus on the business logic relationships between your data.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrap-up">Wrap up<a href="https://skiplabs.io/blog/backend_pressure#wrap-up" class="hash-link" aria-label="Direct link to Wrap up" title="Direct link to Wrap up" translate="no">​</a></h2>
<p>Reactive programming addresses backend pressure by shifting computation from request time to background processing. This approach enables instant response times, automatic cache consistency, and granular updates that scale with system complexity.
The fundamental insight is treating complex computations as persistent, automatically maintained data structures rather than transient operations. This perspective changes how systems handle scaling, caching, and data consistency, often simplifying architecture while improving performance.</p>
<p>If you're dealing with read-heavy workloads where the same complex computations serve multiple requests, reactive approaches offer a compelling alternative to traditional request-response patterns. Start by identifying your most computationally expensive read operations—those are prime candidates for reactive transformation. Look for scenarios where you're repeatedly calculating the same results, dealing with complex cache invalidation logic, or struggling with response times under load.
The challenge has always been the complexity of building and managing these reactive systems manually. Skip changes this by automatically handling the stream creation, dependency tracking, and incremental updates that make reactive backends practical. Instead of spending weeks building reactive infrastructure, you can focus on declaring the relationships between your data and let Skip generate the optimal streaming architecture.</p>
<p>The reactive paradigm isn't a silver bullet, but for systems facing mounting backend pressure from concurrent requests and complex computations, Skip provides a viable path toward managing computational load without sacrificing response times or data consistency. As your system complexity grows, the benefits of automatic dependency management and incremental updates become increasingly valuable.
In our next post, we'll dive deep into load testing Skip-powered systems and share concrete performance data that demonstrates these benefits in practice. We'll explore how Skip's reactive architecture behaves under various load patterns and what metrics matter most when evaluating performance.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dynamically scaling your Skip services]]></title>
            <link>https://skiplabs.io/blog/horizontal-scaling</link>
            <guid>https://skiplabs.io/blog/horizontal-scaling</guid>
            <pubDate>Fri, 06 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How to horizontally scale Skip services with Kubernetes]]></description>
            <content:encoded><![CDATA[<p>Skip makes it fast and easy to build reactive services which efficiently update
their outputs in response to input changes, powering real-time features and
saving resources by avoiding unnecessary recomputations.</p>
<p>The technical foundation that makes this possible is Skip's hyper-efficient
reactive computation graph, which is written in the Skip programming language,
runs natively, and takes great pains to efficiently represent and manipulate
the data underlying your reactive system.</p>
<p>However, that still requires memory, compute, and other resources -- so what do
you do when traffic spikes or grows beyond the capacity of even a powerful
single machine? Scale out and distribute your reactive service across more
machines, of course!</p>
<p>We've recently built out some capabilities to make this easy, using a
distributed leader/follower architecture to dramatically increase the number of
concurrent resource instances that a Skip service can support.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="leaderfollower-architecture">Leader/Follower architecture<a href="https://skiplabs.io/blog/horizontal-scaling#leaderfollower-architecture" class="hash-link" aria-label="Direct link to Leader/Follower architecture" title="Direct link to Leader/Follower architecture" translate="no">​</a></h2>
<p>A single Skip service consists of:</p>
<ul>
<li class="">
<p>a <strong>shared computation graph</strong> representing the portion of that service's
computations that is common among all of its clients: some data structures,
aggregations, partially-computed results, or the like that are always
maintained up-to-date regardless of client requests, and</p>
</li>
<li class="">
<p>one or more <strong>resources</strong> which can be instantiated by clients, dynamically
extending the computation graph as needed to produce data streams customized
by user-specific context or request parameters.</p>
</li>
</ul>
<p>In practice, the work of maintaining the shared computation graph should not
massively increase under spiking load, but the work of maintaining resource
instances and serving their data streams <em>will</em> increase in proportion to the
number of concurrent clients.</p>
<p>This dynamic affords an opportunity for <strong>horizontal scaling</strong>: we can maintain the
shared computation graph just once on a
<a href="https://skiplabs.io/docs/api/helpers/functions/asLeader" target="_blank" rel="noopener noreferrer" class=""><em>leader</em></a> and mirror
it to each of any number of
<a href="https://skiplabs.io/docs/api/helpers/functions/asFollower" target="_blank" rel="noopener noreferrer" class=""><em>followers</em></a>,
among which resource instances are evenly distributed, as illustrated in the
following diagram:</p>
<p><img decoding="async" loading="lazy" alt="Leader-follower architecture" src="https://skiplabs.io/assets/images/leader_follower_arch-381167f19ded784eba293fa2178c43ce.png" width="1218" height="1025" class="img_ev3q"></p>
<p>This diagram shows the structure of a distributed Skip service and the data flow
for a single client request. When client A requests a live data stream, a
reverse proxy forwards that to an available follower, selecting follower 2 in
this example. That follower then sets up a reactive compute graph to maintain
the requested data, incorporating user context and query parameters from client
A as well as any shared inputs from the leader.</p>
<p>This design allows to instantiate many more resources for many more clients than
a single Skip service could handle alone, while maintaining the clean/simple
semantics and low latency of a single-service deployment.</p>
<p>To see this in action, you can pull our example, run it locally, and navigate to
<code>localhost</code> in your browser:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service hackernews --example hackernews --verbose</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">docker compose -f hackernews/compose.distributed.yml up --build</span><br></div></code></pre></div></div>
<p>To see more options or run the application in alternative configurations,
consult the <code>README.md</code>
<a href="https://github.com/SkipLabs/skip/tree/main/examples/hackernews/README.md" target="_blank" rel="noopener noreferrer" class="">online</a>
or in the <code>hackernews</code> directory created by <code>create-skip-service</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="kubernetes">Kubernetes<a href="https://skiplabs.io/blog/horizontal-scaling#kubernetes" class="hash-link" aria-label="Direct link to Kubernetes" title="Direct link to Kubernetes" translate="no">​</a></h2>
<p>Running a distributed reactive service is a great way to handle larger amounts
of traffic and/or more complex reactive computations, but what's really
important is to be able to easily scale your reactive system up and down when
your product goes viral, traffic spikes, and your pager goes off in the middle
of the night.</p>
<p>We've recently built out some Kubernetes
<a href="https://github.com/SkipLabs/skip/tree/main/examples/hackernews/kubernetes/distributed_skip" target="_blank" rel="noopener noreferrer" class="">configuration</a>
to make this as easy as running <code>kubectl scale --replicas=$X ...</code> (or the
GUI/dashboard equivalent if you prefer or are running on a hosted platform)
without breaking any clients or requiring any changes in your reactive service
or other backend components.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Try it yourself! Run the hackernews example linked above using its Kubernetes
configuration, then try running <code>kubectl scale --replicas=$REPLICAS statefulset rhn-skip</code> with varying number of <code>REPLICAS</code> (at least 2, for one leader and one
follower) and see your Skip service scale up and down without downtime.</p></div></div>
<p>The core idea is simple: your reactive Skip service is a Kubernetes
<a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/" target="_blank" rel="noopener noreferrer" class="">StatefulSet</a>,
giving each pod a stable and unique network identity.  When a new pod is added
(either at startup or when scaling up), it registers itself with the cluster's
ingress load balancer.</p>
<p>When a resource is instantiated, the resulting data stream's identifier encodes
the follower hosting the stream, allowing the load balancer to route external
traffic to the proper Skip instance.  When traffic subsides and the deployment
scales down, the pod is taken out of rotation by the load balancer, until a
subsequent scale-up brings it back into use.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrap-up">Wrap-up<a href="https://skiplabs.io/blog/horizontal-scaling#wrap-up" class="hash-link" aria-label="Direct link to Wrap-up" title="Direct link to Wrap-up" translate="no">​</a></h2>
<p>Everyone's infrastructure and application are different, so let us know if there
are other frameworks or tools you'd like to see supported or used in our
examples and demos!</p>
<p>We're also happy to help you scale out your reactive service using Skip, either
by adapting these tools to your environment or advising on your setup.  Reach
out and show us what you're building, or come join the
<a href="https://discord.gg/ss4zxfgUBH" target="_blank" rel="noopener noreferrer" class="">Discord</a>!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React + Vite in front of a Skip Service - the template]]></title>
            <link>https://skiplabs.io/blog/skip_with_react_vite</link>
            <guid>https://skiplabs.io/blog/skip_with_react_vite</guid>
            <pubDate>Fri, 30 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Build a modern chat app with real-time updates using Skip's reactive data system, React, and Vite]]></description>
            <content:encoded><![CDATA[<p>Hello skippers. Today, we are introducing a new template to our collection: React + Vite. While our previous blog posts focused on building skip services, this new template represents our first step into the frontend, bringing together the best of both worlds: front and back.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-skip-chat --template with_react_vite</span><br></div></code></pre></div></div>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-inside-the-template">What's Inside the Template?<a href="https://skiplabs.io/blog/skip_with_react_vite#whats-inside-the-template" class="hash-link" aria-label="Direct link to What's Inside the Template?" title="Direct link to What's Inside the Template?" translate="no">​</a></h2>
<p>Our React + Vite template for Skip Services comes with everything you need to get started on building a modern web application: an easy to manipulate end-to-end data flow from the user interface to the backend, and vice-versa (reactive programming enthusiasts, I have your attention now).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="getting-started">Getting Started<a href="https://skiplabs.io/blog/skip_with_react_vite#getting-started" class="hash-link" aria-label="Direct link to Getting Started" title="Direct link to Getting Started" translate="no">​</a></h2>
<p>To create a new project with our React + Vite template, simply run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-skip-chat --template with_react_vite --verbose</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd my-skip-chat   # enter the project directory</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">./setup.sh        # and set up the project</span><br></div></code></pre></div></div>
<p>Setting up the project will end up with:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">✅ Setup complete!</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">To run the application:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">1. Start the reactive service:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   cd reactive_service &amp;&amp; pnpm start</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">2. In a new terminal, start the frontend:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   cd frontend &amp;&amp; pnpm dev</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">The frontend will be available at http://localhost:5173</span><br></div></code></pre></div></div>
<p>This could hardly ever be simpler :-p</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="if-you-open-the-project-directory">If you open the project directory<a href="https://skiplabs.io/blog/skip_with_react_vite#if-you-open-the-project-directory" class="hash-link" aria-label="Direct link to If you open the project directory" title="Direct link to If you open the project directory" translate="no">​</a></h2>
<p>In both subprojects, <code>frontend</code> and <code>reactive_service</code>, we've hardcoded some values, and taken shortcuts here and there in order to be straight to the point: making reactive programming easy to get, easy to grasp, easy to adopt, easy to embrace. easy to marry... ok, one step too far.</p>
<p>One or two cool things you will want to try:</p>
<ul>
<li class="">plugging into a DB from the backend, we can give you some help : <a href="https://skiplabs.io/blog/postgresql_and_skip" target="_blank" rel="noopener noreferrer" class="">Building a Real-time Blog with Skip and PostgreSQL</a></li>
<li class="">having several conversations on screen, one per tab</li>
<li class="">integrating your user-management system</li>
<li class="">integrating an AI agent to discuss into the chat</li>
</ul>
<p>We have a <a href="https://discord.gg/ss4zxfgUBH" target="_blank" rel="noopener noreferrer" class="">Discord server</a>, why don't you swing by and say hi, ask a question or two?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's Next?<a href="https://skiplabs.io/blog/skip_with_react_vite#whats-next" class="hash-link" aria-label="Direct link to What's Next?" title="Direct link to What's Next?" translate="no">​</a></h2>
<p>For the next Skip article, what should I tackle first? You tell me!</p>
<ul>
<li class="">Scaling your Skip service horizontally?</li>
<li class="">Having an AI agent interacting in the chat ?</li>
<li class="">Managing authorization and privacy per user?</li>
<li class="">What else would be most useful to you now?</li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A CLI to streamline the creation of Skip services]]></title>
            <link>https://skiplabs.io/blog/announcing_create_skip_service_cli</link>
            <guid>https://skiplabs.io/blog/announcing_create_skip_service_cli</guid>
            <pubDate>Fri, 23 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A CLI designed to streamline the creation and initialization of Skip services]]></description>
            <content:encoded><![CDATA[<p>Hello Skippers. Today, we are excited to announce the release of <code>create-skip-service</code>: A CLI Tool for Skip Service Development. It aims to simplify the development workflow by providing a convenient way to bootstrap new Skip services.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service &lt;project-name&gt; &lt;options&gt;</span><br></div></code></pre></div></div>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-are-we-talking-about">What are we talking about?<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#what-are-we-talking-about" class="hash-link" aria-label="Direct link to What are we talking about?" title="Direct link to What are we talking about?" translate="no">​</a></h2>
<p><code>create-skip-service</code> is a CLI tool that helps developers quickly set up new Skip service projects with customizable templates, saving the time you'd need to write boilerplate code. It's designed to follow best practices and provide a consistent starting point for all Skip service development.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-features">Key Features<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#key-features" class="hash-link" aria-label="Direct link to Key Features" title="Direct link to Key Features" translate="no">​</a></h2>
<ul>
<li class=""><strong>Quick Project Setup</strong>: A one-liner to have a fully working service</li>
<li class=""><strong>Template System</strong>: Pick the right template for you, ask us for templates you need</li>
<li class=""><strong>Git Integration</strong>: Automatic Git repository initialization (optional)</li>
<li class=""><strong>Customizable</strong>: Once installed, templates are meant to be enhanced for YOUR project</li>
<li class=""><strong>Verbose Logging</strong>: Detailed output for debugging and understanding the setup process, so you know what's going on</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="getting-started">Getting Started<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#getting-started" class="hash-link" aria-label="Direct link to Getting Started" title="Direct link to Getting Started" translate="no">​</a></h2>
<p><a href="https://en.wikipedia.org/wiki/KISS_principle" target="_blank" rel="noopener noreferrer" class="">KISS</a> - here is the one-liner to get started:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-service</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="basic-usage">Basic Usage<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#basic-usage" class="hash-link" aria-label="Direct link to Basic Usage" title="Direct link to Basic Usage" translate="no">​</a></h3>
<p>Of course, we've kept in mind that not all projects are alike, so here are a few options to get what's right for you:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># Create a new service with default template</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-service</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Create a service with a postgres integration</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-service --template with_postgres</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Create a service without Git initialization</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-service --nogit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Run with verbose logging</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-service --verbose</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="available-options">Available Options<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#available-options" class="hash-link" aria-label="Direct link to Available Options" title="Direct link to Available Options" translate="no">​</a></h2>
<ul>
<li class=""><code>--template, -t</code>: Specify the template to use (default: "default")</li>
<li class=""><code>--nogit</code>: To NOT initialize a Git repository, otherwise git is initialized with a first commit</li>
<li class=""><code>--verbose, -v</code>: Run with verbose logging</li>
<li class=""><code>--quiet, -q</code>: Run with minimal logging</li>
<li class=""><code>--force, -f</code>: Force overwrite if directory exists</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-create-skip-service">Why create-skip-service?<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#why-create-skip-service" class="hash-link" aria-label="Direct link to Why create-skip-service?" title="Direct link to Why create-skip-service?" translate="no">​</a></h2>
<p>We created this tool to address several common challenges in Skip service development:</p>
<ol>
<li class=""><strong>Efficiency</strong>: Reduce the time spent on project setup and configuration</li>
<li class=""><strong>Standardization</strong>: Provide a unified way to create and manage Skip services</li>
<li class=""><strong>Flexibility</strong>: Support different templates and configurations while maintaining consistency</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="contributing">Contributing<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#contributing" class="hash-link" aria-label="Direct link to Contributing" title="Direct link to Contributing" translate="no">​</a></h2>
<p>We welcome contributions to <code>create-skip-service</code>! Whether it's bug reports, feature requests, or code contributions, your input helps make this tool better for everyone.</p>
<ul>
<li class="">GitHub Repository: <a href="https://github.com/SkipLabs/create-skip-service" target="_blank" rel="noopener noreferrer" class="">SkipLabs/create-skip-service</a></li>
<li class="">Issues: <a href="https://github.com/SkipLabs/create-skip-service/issues" target="_blank" rel="noopener noreferrer" class="">GitHub Issues</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's Next?<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#whats-next" class="hash-link" aria-label="Direct link to What's Next?" title="Direct link to What's Next?" translate="no">​</a></h2>
<p>We're actively working on improving <code>create-skip-service</code> with plans for:</p>
<ul>
<li class="">Additional template options</li>
<li class="">Enhanced configuration options</li>
<li class="">More detailed documentation</li>
<li class="">Community-driven template contributions</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-it-out">Try It Out!<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#try-it-out" class="hash-link" aria-label="Direct link to Try It Out!" title="Direct link to Try It Out!" translate="no">​</a></h2>
<p>Give it a go! And keep us posted on what you are building. Feedback is bliss, right?</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx create-skip-service my-service</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrapping-up">Wrapping Up<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#wrapping-up" class="hash-link" aria-label="Direct link to Wrapping Up" title="Direct link to Wrapping Up" translate="no">​</a></h2>
<p><code>create-skip-service</code> represents our commitment to making Skip service development more efficient and enjoyable. By handling the boilerplate setup automagically<sup><a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#user-content-fn-1-9cae41" id="user-content-fnref-1-9cae41" data-footnote-ref="true" aria-describedby="footnote-label" class="anchorTargetStickyNavbar_Vzrq">1</a></sup>, we believe this tool will help developers focus on what matters most - building great services.</p>
<p>As of today, we are already on our way to improving and augmenting the number of options and templates. Come back and help us sort priorities!</p>
<p>We have a <a href="https://discord.gg/ss4zxfgUBH" target="_blank" rel="noopener noreferrer" class="">Discord server</a>, why don't you swing by and say hi?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next-1">What's Next?<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#whats-next-1" class="hash-link" aria-label="Direct link to What's Next?" title="Direct link to What's Next?" translate="no">​</a></h2>
<p>For the next Skip article, what should I tackle first? You tell me!</p>
<ul>
<li class="">Scaling your Skip service horizontally?</li>
<li class="">Integrating with frontend frameworks like React?</li>
<li class="">Managing authorization and privacy per user?</li>
<li class="">What else would be most useful to you <em>now</em>?</li>
</ul>
<!-- -->
<section data-footnotes="true" class="footnotes"><h2 class="anchor anchorTargetStickyNavbar_Vzrq sr-only" id="footnote-label">Footnotes<a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#footnote-label" class="hash-link" aria-label="Direct link to Footnotes" title="Direct link to Footnotes" translate="no">​</a></h2>
<ol>
<li class="anchorTargetStickyNavbar_Vzrq" id="user-content-fn-1-9cae41">
<p>not a typo ;-) <a href="https://skiplabs.io/blog/announcing_create_skip_service_cli#user-content-fnref-1-9cae41" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Real-time Blog with Skip and PostgreSQL]]></title>
            <link>https://skiplabs.io/blog/postgresql_and_skip</link>
            <guid>https://skiplabs.io/blog/postgresql_and_skip</guid>
            <pubDate>Fri, 16 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How to use Skip's reactive data streaming with a PostgreSQL backend]]></description>
            <content:encoded><![CDATA[<p>Hello Skippers. Today, we are going to plug a <a href="https://www.postgresql.org/" target="_blank" rel="noopener noreferrer" class="">PostgreSQL</a> service and our starting point is the following project on GitHub: <a href="https://github.com/SkipLabs/postgres_and_skip_poc" target="_blank" rel="noopener noreferrer" class="">Skip Postgres Demo</a>. We will demonstrate how to use Skip's reactive data streaming with a PostgreSQL backend. This project implements a simple blog post system with real-time updates using Skip's streaming capabilities.</p>
<p><img decoding="async" loading="lazy" alt="PostgreSQL and Skip Service" src="https://skiplabs.io/assets/images/postgres_and_skip-1ba0e6630b14176bfc008a3280a6b234.png" width="1270" height="361" class="img_ev3q"></p>
<!-- -->
<p>Before we get too far ahead, two things…(1) what you need and (2) what you will learn:</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-you-need">What you need:<a href="https://skiplabs.io/blog/postgresql_and_skip#what-you-need" class="hash-link" aria-label="Direct link to What you need:" title="Direct link to What you need:" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://nodejs.org/en" target="_blank" rel="noopener noreferrer" class="">Node.js</a> (Latest LTS version recommended)</li>
<li class=""><a href="https://pnpm.io/" target="_blank" rel="noopener noreferrer" class=""><code>pnpm</code></a> package manager</li>
<li class=""><a href="https://www.docker.com/" target="_blank" rel="noopener noreferrer" class="">Docker</a> (for PostgreSQL)</li>
<li class="">(optional) a Json pretty printer in the terminal, I use <a href="https://jqlang.org/" target="_blank" rel="noopener noreferrer" class="">jq</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-you-will-learn">What you will learn:<a href="https://skiplabs.io/blog/postgresql_and_skip#what-you-will-learn" class="hash-link" aria-label="Direct link to What you will learn:" title="Direct link to What you will learn:" translate="no">​</a></h2>
<ul>
<li class="">Real-time post streaming using Skip's reactive data system</li>
<li class="">PostgreSQL integration for data persistence</li>
<li class="">RESTful API endpoints for post management</li>
<li class="">Support for streaming data access</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="okayreadylets-go">Okay..ready?….let's go!<a href="https://skiplabs.io/blog/postgresql_and_skip#okayreadylets-go" class="hash-link" aria-label="Direct link to Okay..ready?….let's go!" title="Direct link to Okay..ready?….let's go!" translate="no">​</a></h2>
<p>We are starting from a REST server for the simplest blog post system and we are going to add real-time updates features to it.
Let's check out the project:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git clone https://github.com/SkipLabs/postgres_and_skip_poc.git</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd postgres_and_skip_poc/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git checkout $(git rev-list --max-parents=0 main)</span><br></div></code></pre></div></div>
<p>And yes, you are right we are not starting on top of the main branch, but don't worry we'll get there in no time.</p>
<p>Run the <code>init_and_start_server.sh</code> script, it will start a PostgreSQL database in a Docker container and fill it in with some data. It will then install and build the whole project with <code>pnpm</code> to finally start it!</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">./init_and_start_server.sh</span><br></div></code></pre></div></div>
<p>The last lines should look something like:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">🚀 Starting server...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&gt; skip-postgres@1.0.0 start /your/path/skip-postgres</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&gt; node dist/index.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Server running at http://localhost:3000</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div></code></pre></div></div>
<p>In a new terminal, we are going to check that the REST API is up:
First, let's display one of the articles:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl localhost:3000/posts/7 | jq</span><br></div></code></pre></div></div>
<p>Note that here there's an easy shortcut - I use <a href="https://jqlang.org/" target="_blank" rel="noopener noreferrer" class=""><code>jq</code></a>, which will make life more convenient to read JSON output.</p>
<p>As you can see, the article is not published:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"published_at"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token null keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"created_at"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;some_date&gt;"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"updated_at"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;some_date&gt;"</span><br></div></code></pre></div></div>
<p>We are going to publish and unpublish it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -X PATCH http://localhost:3000/posts/7/publish | jq</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">curl -X PATCH http://localhost:3000/posts/7/unpublish | jq</span><br></div></code></pre></div></div>
<p>You should see the modification as results of the calls.</p>
<p>A working REST API.</p>
<p>And now here comes the fun part, let's make it a reactive API</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-skip-service">The Skip Service<a href="https://skiplabs.io/blog/postgresql_and_skip#the-skip-service" class="hash-link" aria-label="Direct link to The Skip Service" title="Direct link to The Skip Service" translate="no">​</a></h2>
<p>First of all, kill your running server.
We are about to checkout the top of the main branch and install the necessary bits:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git checkout main</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pnpm add -D @skipruntime/core @skipruntime/helpers @skipruntime/server</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pnpm add @skip-adapter/postgres</span><br></div></code></pre></div></div>
<p>A Skip service has collections as inputs and mappers to transform them into resources as outputs.</p>
<p>In our case, in file <a href="https://github.com/SkipLabs/postgres_and_skip_poc/blob/main/src/skipservice.ts" target="_blank" rel="noopener noreferrer" class=""><code>skipservice.ts</code></a> you will find:</p>
<ol>
<li class="">
<p>A type you want to be notified about: <a href="https://github.com/SkipLabs/postgres_and_skip_poc/blob/main/src/skipservice.ts#L16-L29" target="_blank" rel="noopener noreferrer" class=""><code>PostWithAuthor</code></a> which contains information from two tables: Users and Posts (see <a href="https://github.com/SkipLabs/postgres_and_skip_poc/blob/main/src/db/schema.sql" target="_blank" rel="noopener noreferrer" class=""><code>schema.sql</code></a>).</p>
</li>
<li class="">
<p>A mapper <a href="https://github.com/SkipLabs/postgres_and_skip_poc/blob/main/src/skipservice.ts#L32-L65" target="_blank" rel="noopener noreferrer" class=""><code>PostMapper</code></a> which creates instances of this type from an <code>EagerCollection</code> (an <code>EagerCollection</code> is always kept up-to-date).</p>
</li>
<li class="">
<p>A resource <a href="https://github.com/SkipLabs/postgres_and_skip_poc/blob/main/src/skipservice.ts#L73-L85" target="_blank" rel="noopener noreferrer" class=""><code>PostResource</code></a> which implements a function from a <a href="https://github.com/SkipLabs/postgres_and_skip_poc/blob/main/src/skipservice.ts#L67-L69" target="_blank" rel="noopener noreferrer" class=""><code>PostsResourceInputs</code></a> to an <code>EagerCollection&lt;number, PostWithAuthor&gt;</code></p>
</li>
</ol>
<p>And it is when we instantiate our service that we plug it to the PostgreSQL, when we define an instance of <a href="https://github.com/SkipLabs/reactive_social_network_service_poc/blob/main/src/skipservice.mts#L10-L19" target="_blank" rel="noopener noreferrer" class=""><code>SkipService</code></a>, we receive a context as input. This context provides a method <code>useExternalResource</code>.</p>
<p>Remember that in Skip, Resources are the outputs of your service that clients can subscribe to, while Collections are the inputs that feed data into your service. When you create a graph using <code>createGraph</code>, you define how these Collections are transformed into Resources. Once you've set up your graph and connected it to your external services (like PostgreSQL in our case), you just need a few lines of code in your server to expose these Resources to clients:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/streams/posts/:uid'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">asyncHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> uid </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Number</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> uuid </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> serviceBroker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getStreamUUID</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'posts'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> uid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">redirect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">301</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation constant" style="color:#36acaa">SKIP_READ_URL</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">/v1/streams/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">uuid</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/streams/posts'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">asyncHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> uuid </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> serviceBroker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getStreamUUID</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'posts'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">redirect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">301</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation constant" style="color:#36acaa">SKIP_READ_URL</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">/v1/streams/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">uuid</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>You can find these lines in <a href="https://github.com/SkipLabs/postgres_and_skip_poc/blob/main/src/index.ts#L65-L80" target="_blank" rel="noopener noreferrer" class=""><code>src/index.ts#L65-L80</code></a></p>
<p>We are adding two endpoints:</p>
<ul>
<li class=""><code>/streams/posts/:uid</code> which is a stream of changes for a given post of id uid</li>
<li class=""><code>/streams/posts</code> which is a stream of changes for the entire posts table</li>
</ul>
<p>Let's play with it, in three terminals:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="terminal-1-start-the-server">Terminal 1: Start the Server<a href="https://skiplabs.io/blog/postgresql_and_skip#terminal-1-start-the-server" class="hash-link" aria-label="Direct link to Terminal 1: Start the Server" title="Direct link to Terminal 1: Start the Server" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">./init_and_start_server.sh</span><br></div></code></pre></div></div>
<p>Server and services are ready when you see:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Skip control service listening on port 8081</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Skip streaming service listening on port 8080</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Server running at http://localhost:3000</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="terminal-2-subscribe-to-post-updates">Terminal 2: Subscribe to Post Updates<a href="https://skiplabs.io/blog/postgresql_and_skip#terminal-2-subscribe-to-post-updates" class="hash-link" aria-label="Direct link to Terminal 2: Subscribe to Post Updates" title="Direct link to Terminal 2: Subscribe to Post Updates" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -LN http://localhost:3000/streams/posts/7 | \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  while read -r line; do</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    echo "$line" | grep '^data:' | sed 's/^data: //' | jq .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  done</span><br></div></code></pre></div></div>
<blockquote>
<p>💡 <strong>Note</strong>: The shell command above includes some formatting magic to make the output readable. It filters for data lines and pretty-prints the JSON using <code>jq</code>.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="terminal-3-modify-the-post">Terminal 3: Modify the Post<a href="https://skiplabs.io/blog/postgresql_and_skip#terminal-3-modify-the-post" class="hash-link" aria-label="Direct link to Terminal 3: Modify the Post" title="Direct link to Terminal 3: Modify the Post" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -X PATCH http://localhost:3000/posts/7/publish | jq</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">curl -X PATCH http://localhost:3000/posts/7/unpublish | jq</span><br></div></code></pre></div></div>
<p>Watch Terminal 2 while you run these commands in Terminal 3 - you'll see the real-time updates!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrap-up">Wrap-up<a href="https://skiplabs.io/blog/postgresql_and_skip#wrap-up" class="hash-link" aria-label="Direct link to Wrap-up" title="Direct link to Wrap-up" translate="no">​</a></h2>
<p>In our <a href="https://skiplabs.io/blog/reactive_social_network_service_poc" target="_blank" rel="noopener noreferrer" class="">previous blog post</a>, we have walked you through how to create a Skip service for a reactive social network, with code! Check it out here: <a href="https://github.com/SkipLabs/reactive_social_network_service_poc" target="_blank" rel="noopener noreferrer" class="">Reactive Social Network Service (Proof of Concept)</a>.</p>
<p>Now we have learned how to plug a PostgreSQL service using Skip!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's Next?<a href="https://skiplabs.io/blog/postgresql_and_skip#whats-next" class="hash-link" aria-label="Direct link to What's Next?" title="Direct link to What's Next?" translate="no">​</a></h2>
<p>For the next Skip article, what should I tackle first? You tell me!</p>
<ul>
<li class="">Scaling your Skip service horizontally?</li>
<li class="">Integrating with frontend frameworks like React?</li>
<li class="">Managing authorization and privacy per user?</li>
<li class="">What else would be most useful to you NOW?</li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reactive Social Network Service with Skip]]></title>
            <link>https://skiplabs.io/blog/reactive_social_network_service_poc</link>
            <guid>https://skiplabs.io/blog/reactive_social_network_service_poc</guid>
            <pubDate>Wed, 30 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Build step by step a skip service]]></description>
            <content:encoded><![CDATA[<p>Reactive programming often sounds complex—but it doesn't have to be. What if you could actually see how data responds to change, in real time, right from your terminal?</p>
<p>In this tutorial, we walk through building a small proof-of-concept social network backend using <a href="https://www.skiplabs.io/" target="_blank" rel="noopener noreferrer" class="">Skip</a> and TypeScript. It tracks users, groups, and their friendships—with automatic updates to "active friends" using Skip’s reactive computation graph.</p>
<p>You’ll learn how to:</p>
<ul>
<li class="">Set up a reactive Skip service</li>
<li class="">Define users and groups as live-updating resources</li>
<li class="">Connect it all to a REST API using Express</li>
<li class="">Observe real-time updates as the data evolves</li>
</ul>
<blockquote>
<p>"The simpler, the merrier" — this project keeps things minimal, focused, and easy to explore.</p>
</blockquote>
<p>From Alice adding a new friend to live data reactions, this guide makes reactive systems tangible.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-simpler-the-better">The Simpler, the Better<a href="https://skiplabs.io/blog/reactive_social_network_service_poc#the-simpler-the-better" class="hash-link" aria-label="Direct link to The Simpler, the Better" title="Direct link to The Simpler, the Better" translate="no">​</a></h3>
<p>Ever heard the saying, "The simpler, the better"? Well, that especially holds true when it comes to learning—particularly in reactive programming. Think of it like what we expect from a social network: <em>reactivity</em> in response to change. Sure, a full-blown social network has more bells and whistles than a carnival, but here we're just tackling a small piece of it. This guide keeps things light and breezy, focusing on the essentials to show how to build a reactive system using <strong>Skip</strong>. It's a proof of concept.</p>
<p>By keeping things simple and working right in the terminal, we can easily follow how data flows and how changes propagate without getting lost in complexity.</p>
<p>We'll start by setting up a Node project with TypeScript. Then, we'll define our data types. Since this is a social network, we want to represent friends and determine if they're active in a given group. We'll define members with names and associated groups, and for each group, we'll track whether a given member is active. After that, we'll build the Skip service, and finally, we'll wrap things up with a web server that uses the reactive service. All this will be wrapped up in an example where Alice makes a new friend.</p>
<p>You can follow along with <a href="https://github.com/SkipLabs/reactive_social_network_service_poc" target="_blank" rel="noopener noreferrer" class="">the full code on GitHub</a>, structured with a commit for each step:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1-the-typescript-project">Step 1: The TypeScript Project<a href="https://skiplabs.io/blog/reactive_social_network_service_poc#step-1-the-typescript-project" class="hash-link" aria-label="Direct link to Step 1: The TypeScript Project" title="Direct link to Step 1: The TypeScript Project" translate="no">​</a></h3>
<p>The whole first step can be found <a href="https://github.com/SkipLabs/reactive_social_network_service_poc/commit/b424165666b45a9d17b969f2422941f748556c85" target="_blank" rel="noopener noreferrer" class="">here on GitHub</a>.</p>
<p>This first project really is Basic TypeScript 101, but watch out for some details such as the configuration of <code>package.json</code> and <code>tsconfig.json</code>. These two files can be sneaky little configuration traps if you're not careful. They might seem straightforward, but trust me, they can trip you up when you least expect it. So, keep an eye out and make sure everything is set up just right.</p>
<p>Let's create a directory for this proof-of-concept:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">mkdir reactive_social_network_service</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd reactive_social_network_service</span><br></div></code></pre></div></div>
<p>And now the TypeScript project:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm init -y</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm install --save-dev typescript @types/node</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mkdir -p src</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npx tsc --init \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    --target ES2022 \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    --module nodenext \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    --rootDir "./src" \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    --outDir "./dist" \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    --moduleResolution nodenext</span><br></div></code></pre></div></div>
<p>Configuration of <code>package.json</code> to prepare the project for what's about to come:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm pkg set type="module"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm pkg set scripts.build="tsc"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm pkg set scripts.start="node dist/index.js"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm pkg set scripts.clean="rm -rf dist node_modules"</span><br></div></code></pre></div></div>
<p>We are now ready to enter the Skip world!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2-the-skip-service">Step 2: The Skip Service<a href="https://skiplabs.io/blog/reactive_social_network_service_poc#step-2-the-skip-service" class="hash-link" aria-label="Direct link to Step 2: The Skip Service" title="Direct link to Step 2: The Skip Service" translate="no">​</a></h3>
<p>The whole second step can be found <a href="https://github.com/SkipLabs/reactive_social_network_service_poc/commit/a63289aaae2404e62d1d212d870be042ab47f3e5" target="_blank" rel="noopener noreferrer" class="">here on GitHub</a>.</p>
<p>We are going to need SkipLabs packages:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm add @skiplabs/skip</span><br></div></code></pre></div></div>
<p>So you better understand where we are heading, here is what the source directory will eventually look like:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">src</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    activefriends.mts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    data.mts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    index.ts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    skipservice.mts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    types.mts</span><br></div></code></pre></div></div>
<p>And we start with the necessary types. In a nutshell, Skip defines a resource as the output of a Skip service. A resource is updated and maintained reactively. A collection is the core data structure over which reactive computations operate. An <code>EagerCollection</code> is always kept up-to-date. <code>LazyCollection</code> exists as well for evaluation upon queries only, but we are not using them in this proof-of-concept.</p>
<p>Here the code for <code>src/types.mts</code>:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">EagerCollection</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@skipruntime/core"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">UserID</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">GroupID</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">User</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> active</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">boolean</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> friends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">Group</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> members</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">ServiceInputs</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    users</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">UserID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> User</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    groups</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Group</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">ResourceInputs</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    users</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">UserID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> User</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    activeMembers</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> UserID</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    UserID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    User</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Group</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ServiceInputs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ResourceInputs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Now some data to get started. Outside this proof-of-concept, this would come from your database.</p>
<p>Four users and two groups e.g. Carol is in both groups but inactive.</p>
<p>Here is the code for <code>src/data.mts</code>:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> InitialData </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@skipruntime/core"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ServiceInputs </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./types.mjs"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Initial data for the social network service</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> initialData</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> InitialData</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ServiceInputs</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    users</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Bob"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> active</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> friends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Alice"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> active</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> friends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Carol"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> active</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> friends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Eve"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> active</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> friends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    groups</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1001</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Group 1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> members</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1002</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Group 2"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> members</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> initialData </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Let's talk about active friends! For that, we need to define a filter for that <code>active</code> boolean. We are going to use a mapper. In Skip, a mapper describes a computation from keys/values in one collection to keys/values in another collection. It forms the edges of the Skip reactive computation graph, specifying transformations and compositions of data to produce intermediate results and outputs.</p>
<p>We are defining two mappers and a resource: one mapper to retrieve active members out of a given group, one mapper to retrieve friends of a given user out of a given group, and a resource which will hold active friends for a given user.</p>
<p>Here is the code for <code>src/activefriends.mts</code>:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">EagerCollection</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">Json</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">Mapper</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">Resource</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">Values</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@skipruntime/core"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Group</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> User</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ResourceInputs </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./types.mjs"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Mapper functions for reactive data transformations</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">ActiveMembers</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">implements</span><span class="token plain"> </span><span class="token class-name">Mapper</span><span class="token class-name operator" style="color:#393A34">&lt;</span><span class="token class-name">GroupID</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> Group</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> GroupID</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> UserID</span><span class="token class-name operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> users</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">UserID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> User</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Maps group members to active users only</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">mapEntry</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gid</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> group</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Values</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">Group</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Iterable</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> group</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getUnique</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">members</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">flatMap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">uid</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">users</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getUnique</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">uid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">active </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">gid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> uid</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Filters group members to only include friends of a specific user</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">FilterFriends</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">implements</span><span class="token plain"> </span><span class="token class-name">Mapper</span><span class="token class-name operator" style="color:#393A34">&lt;</span><span class="token class-name">GroupID</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> UserID</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> GroupID</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> UserID</span><span class="token class-name operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">readonly</span><span class="token plain"> user</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> User</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">mapEntry</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gid</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> uids</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Values</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">UserID</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Iterable</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> uids</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toArray</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">flatMap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">uid</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">user</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">friends</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">includes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">uid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">gid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> uid</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Resource that provides active friends for a given user</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">ActiveFriends</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">implements</span><span class="token plain"> </span><span class="token class-name">Resource</span><span class="token class-name operator" style="color:#393A34">&lt;</span><span class="token class-name">ResourceInputs</span><span class="token class-name operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">readonly</span><span class="token plain"> uid</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> UserID</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">params</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Json</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> params </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"number"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Missing required number parameter 'uid'"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uid </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Creates a collection of active friends for the specified user</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">instantiate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">inputs</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ResourceInputs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">GroupID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> UserID</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> user </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">users</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getUnique</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">activeMembers</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">FilterFriends</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> user</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ActiveMembers</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ActiveFriends </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>And now the Skip service itself. It encompasses a server and a broker.</p>
<p>Here is the code for <code>src/skipservice.mts</code>:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> runService </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@skipruntime/server"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> SkipServiceBroker </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@skipruntime/helpers"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ResourceInputs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ServiceInputs </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./types.mjs"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> initialData </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./data.mjs"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> ActiveFriends</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ActiveMembers </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./activefriends.mjs"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Service configuration and reactive graph definition</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> service </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    initialData</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    resources</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> active_friends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ActiveFriends </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Creates the reactive data flow graph</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">createGraph</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ServiceInputs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ResourceInputs </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> users </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">users</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> activeMembers </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">groups</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ActiveMembers</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> users</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> users</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> activeMembers </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Start the reactive service with specified ports</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> server </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">runService</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">service</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    streaming_port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    control_port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8081</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Initialize the service broker for client communication</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> serviceBroker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">SkipServiceBroker</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"localhost"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    control_port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8081</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    streaming_port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> server</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> serviceBroker </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3-the-server">Step 3: The Server<a href="https://skiplabs.io/blog/reactive_social_network_service_poc#step-3-the-server" class="hash-link" aria-label="Direct link to Step 3: The Server" title="Direct link to Step 3: The Server" translate="no">​</a></h3>
<p>The whole first step can be found <a href="https://github.com/SkipLabs/reactive_social_network_service_poc/commit/5fb3308540468243eeb10972d2ace887d5d3678b" target="_blank" rel="noopener noreferrer" class="">here on GitHub</a>.</p>
<p>To use the Skip service, we are creating an <a href="https://expressjs.com/" target="_blank" rel="noopener noreferrer" class="">Express</a> server:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm install express</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm install --save-dev @types/express</span><br></div></code></pre></div></div>
<p>This server will expose an API to monitor active friends, and modify users and groups.</p>
<p>Here is the code for <code>src/index.ts</code>:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> express</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Response </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"express"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> server</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> serviceBroker </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./skipservice.mjs"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Initialize Express app</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> app </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">express</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">use</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">express</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">json</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Store a reference to the Express server</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> expressServer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">listen</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">8082</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token builtin">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Web server listening at port 8082</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Utility function to handle errors</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">handleError</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">unknown</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token builtin">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Error: "</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">status</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">500</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">json</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Route handlers</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">getActiveFriends</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> uuid </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> serviceBroker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getStreamUUID</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"active_friends"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Number</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">redirect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">301</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">http://localhost:8080/v1/streams/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">uuid</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">handleError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">updateEntity</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">entity</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> idParam</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> req</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Number</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">idParam</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> serviceBroker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">entity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">body</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">status</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">200</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">json</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">handleError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Define routes</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/active_friends/:uid"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> getActiveFriends</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/users/:uid"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">updateEntity</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"users"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"uid"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> req</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/groups/:gid"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Response</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">updateEntity</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"groups"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gid"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> req</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Graceful shutdown handler for:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// - SIGINT: Ctrl+C in terminal</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// - SIGTERM: System termination requests (kill command, container orchestration, etc.)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"SIGTERM"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"SIGINT"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sig</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> server</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    expressServer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token builtin">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\nServers shut down."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Let's install, build and start it!</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm install</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm run build</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm run start</span><br></div></code></pre></div></div>
<p>Starting the server will display something like:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&gt; reactive_social_network_service@1.0.0 start</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&gt; node dist/index.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Skip control service listening on port 8081</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Skip streaming service listening on port 8080</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Web server listening at port 8082</span><br></div></code></pre></div></div>
<p>The Express server listens to the steaming service for updates, and it is now time see it in action!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-4-lets-see-it-work">Step 4: Let's See It Work<a href="https://skiplabs.io/blog/reactive_social_network_service_poc#step-4-lets-see-it-work" class="hash-link" aria-label="Direct link to Step 4: Let's See It Work" title="Direct link to Step 4: Let's See It Work" translate="no">​</a></h3>
<p>We are going to use three terminals: one is already running the server and the service, one will issue writes on port 8081, and one will watch changes on port 8082.</p>
<p>While the service is running, in a fresh terminal, we are going to listen for changes on Alice's friends (index <code>1</code>):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -LN http://localhost:8082/active_friends/1 # Alice's active friends</span><br></div></code></pre></div></div>
<p>The previous command will continue listening after displaying the following, hence you won't get any prompt back.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">event: init</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">id: &lt;SOME_HASH&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">data: [[1002,[0]]]</span><br></div></code></pre></div></div>
<p>Leave it like that and open the third and last terminal in which we are going to add and remove values to the initial data: the list of Alice friends is about to evolve:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl http://localhost:8081/v1/inputs/users \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    -X PATCH \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    --json '[[1, [</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        "name": "Alice",</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        "active": true,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        "friends": [0, 2, 3]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    ]]]'</span><br></div></code></pre></div></div>
<p>What do you see in the second terminal? The data is being reactively updated. Go ahead and try other modifications e.g. Bob turns inactive, Alice removes Carol from her group of friends.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="wrapping-up">Wrapping Up<a href="https://skiplabs.io/blog/reactive_social_network_service_poc#wrapping-up" class="hash-link" aria-label="Direct link to Wrapping Up" title="Direct link to Wrapping Up" translate="no">​</a></h3>
<p>Reactive programming doesn't have to be intimidating. With Skip, we've seen how simple it is to wire up reactivity—from raw data all the way to live updates in an API. The idea is simple: when something changes, everything that depends on it updates automatically. And that's exactly what we built.</p>
<p>Of course, this is just the beginning. You now have a foundation for reactive services, and from here, the possibilities are wide open. Want to plug it into a real-time dashboard? Hook it into a game? Build collaborative tools?</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's next?<a href="https://skiplabs.io/blog/reactive_social_network_service_poc#whats-next" class="hash-link" aria-label="Direct link to What's next?" title="Direct link to What's next?" translate="no">​</a></h3>
<p>For the next Skip article, what should I tackle first? You tell me!</p>
<ul>
<li class="">Listening to live changes from a PostgreSQL database?</li>
<li class="">Scaling your Skip service horizontally?</li>
<li class="">Integrating with frontend frameworks like React?</li>
<li class="">Managing authorization and privacy per user?</li>
<li class="">What else would be useful to your projects?</li>
</ul>
<p>We've exposed reactive programming in the nude, 'scuse my french. The reactive world can be vast and complex but SKIP will be your guide and now you've got a solid start.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Event-Hidden Architectures]]></title>
            <link>https://skiplabs.io/blog/event-hidden-arch</link>
            <guid>https://skiplabs.io/blog/event-hidden-arch</guid>
            <pubDate>Mon, 21 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Interactive features without events]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="event-hidden-architectures">Event-Hidden Architectures<a href="https://skiplabs.io/blog/event-hidden-arch#event-hidden-architectures" class="hash-link" aria-label="Direct link to Event-Hidden Architectures" title="Direct link to Event-Hidden Architectures" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-did-we-get-here">How did we get here?<a href="https://skiplabs.io/blog/event-hidden-arch#how-did-we-get-here" class="hash-link" aria-label="Direct link to How did we get here?" title="Direct link to How did we get here?" translate="no">​</a></h3>
<p>One of the most powerful and consistent trends in software for the past decade has been the move from single stack to cloud native applications.  Cloud native applications are inherently distributed and have become popular as developers are drawn to the convenience of containers and platform-as-a-service infrastructure.</p>
<p>The API-ification of important subsystems means today hardly anyone would consider implementing their own payments, shipping, SMS, chat, billing or shopping cart functionality.  Instead they’ll lean on Stripe, Twilio, Shopify, Shippo, etc; accelerating time-to-value and further distributing the functions of the application.</p>
<p>In the last few years developers have moved quickly to incorporate more and diverse AI features into their applications, but AI models and application logic compute requirements are quite different from one another and therefore seldom run in the same stack, further distributing the application.</p>
<p><img decoding="async" loading="lazy" alt="Example architecture diagram" src="https://skiplabs.io/assets/images/event-hidden-arch-diagram-c1aba9f72902f8e42bba5446da846b8f.png" width="1999" height="950" class="img_ev3q"></p>
<p>Some have called for a “back to basics” approach of a single stack or modular monolith, but given the list of external demands on modern application development, distributed is unlikely to go away.  Instead it’s becoming a more common approach for application architectures, not less, especially for “successful” applications that need to modularize to support more customers, users and features.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="10-years-of-bad-advice">10 years of bad advice<a href="https://skiplabs.io/blog/event-hidden-arch#10-years-of-bad-advice" class="hash-link" aria-label="Direct link to 10 years of bad advice" title="Direct link to 10 years of bad advice" translate="no">​</a></h3>
<p>One can sympathize with the desire for a “back to basics” approach to cloud-native applications.  There are many “pros” to writing cloud-native, distributed applications but living with them has been miserable for developers and operators alike.  “Cloud native” is effectively synonymous with “distributed,” and “distributed” is another way to say a network is parked in the middle of the application.  This changes engineering and operations in many big and subtle ways.  The most common and questionable advice to tackle these challenges has been “event-driven.”</p>
<p><img decoding="async" loading="lazy" alt="Events everywhere buzz lightyear" src="https://skiplabs.io/assets/images/event-hidden-arch-buzz-acbaedff9968f8e157c6fd250debb057.png" width="888" height="500" class="img_ev3q"></p>
<p>Events are the biggest reason why engineering teams try to postpone distributed or microservice architectures for as long as possible.  “Cloud native” and “microservices” have unwittingly become synonymous with “event-driven” and sullied their reputations in the process.</p>
<p>Events remain a useful technical mechanism to transmit state and data among application resources but they are a miserable experience for developers and operators alike.  Events:</p>
<ul>
<li class="">Put application state and data in dozens of queues that make the application prone to failures, race conditions and incorrect results</li>
<li class="">Force developers to think and program asynchronously which is more complex and time consuming than synchronous code</li>
<li class="">Require developers know more than just their portion of the code and application domain: they also need to understand how to work with queues, caches and event schemas</li>
<li class="">Make applications difficult to troubleshoot as there is no one place to understand the state and data for an application</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Revolution against API schemas" src="https://skiplabs.io/assets/images/event-hidden-arch-revolution-b2990c52168724f15247afff08c8b1e3.png" width="1024" height="1024" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="event-driven-vs-event-hidden"><del>Event-driven</del> vs event-hidden<a href="https://skiplabs.io/blog/event-hidden-arch#event-driven-vs-event-hidden" class="hash-link" aria-label="Direct link to event-driven-vs-event-hidden" title="Direct link to event-driven-vs-event-hidden" translate="no">​</a></h3>
<p>The event-driven approach <em>was</em> necessary in 2020 but it isn’t any longer in 2025.  Today’s modern abstractions ensure it’s unnecessary to make a faustian bargain between the flexibility &amp; scale of distributed and the existential dread inspired by event-driven architectures.</p>
<p>There are three innovations that together allow us to engineer applications as event-driven but where developers and operators are fully abstracted away from the events themselves.</p>
<ol>
<li class="">
<p><a href="https://react.dev/" target="_blank" rel="noopener noreferrer" class="">React</a> paired with client state management frameworks like <a href="https://redux.js.org/" target="_blank" rel="noopener noreferrer" class="">Redux</a>, <a href="https://xstate.js.org/" target="_blank" rel="noopener noreferrer" class="">Xstate</a> or <a href="https://zustand-demo.pmnd.rs/" target="_blank" rel="noopener noreferrer" class="">Zustand</a> create a simple, productive experience for frontend engineering.  Frontend engineers are able to implement simple, performant UI components that can support complex application behaviors while outsourcing state and DOM management to the framework.</p>
</li>
<li class="">
<p>Durable execution systems like <a href="https://temporal.io/" target="_blank" rel="noopener noreferrer" class="">Temporal</a>, <a href="https://restate.dev/" target="_blank" rel="noopener noreferrer" class="">Restate</a> and <a href="https://www.dbos.dev/" target="_blank" rel="noopener noreferrer" class="">DBOS</a> ensure distributed backend application writes are always correct within and across services and network boundaries because each function call is durable by default.  All of the convenient guarantees of an ACID transaction are injected into the everyday programming experience.</p>
</li>
<li class="">
<p>Reactive frameworks for incremental computation like <a href="https://github.com/SkipLabs/skip" target="_blank" rel="noopener noreferrer" class="">Skip</a> make it simple to implement distributed application reads from multiple sources and latencies to disparate clients and views.  Developers can blend data from multiple REST and/or streaming sources but write their logic as simple map functions free from references to infrastructure or events.  Because the services are reactive, performance and correctness concerns like cache invalidations are outsourced to the framework.</p>
</li>
</ol>
<p>All three technologies share many of the same design principles.  Each technology caters to a different area of the application (client changes, backend writes, backend reads) but they all:</p>
<ul>
<li class="">Provide a declarative developer experiences free from references to infrastructure</li>
<li class="">Are event-driven “under the covers” but don’t leak these details to users</li>
<li class="">Work asynchronously, supporting high levels of concurrency but still make the developer experience feel effectively synchronous</li>
<li class="">Manage and in some cases enforce immutability of functions and/or data</li>
<li class="">Handle application state as a first class concern</li>
<li class="">Allow for modularity and incremental adoption - they don’t enforce “one pure approach” on an entire application</li>
<li class="">Can run locally or globally</li>
<li class="">Are open source</li>
</ul>
<p>Collectively, these frameworks provide a substantial upgrade to applications across developer experience, feature velocity, correctness, efficiency and operational reliability.  Software development returns to a simpler, low context model; developers do not need to learn the unique quirks of low level infrastructure or understand the overall application architecture just to build or enhance their part of the application.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-powers">New powers<a href="https://skiplabs.io/blog/event-hidden-arch#new-powers" class="hash-link" aria-label="Direct link to New powers" title="Direct link to New powers" translate="no">​</a></h3>
<p>Burying events doesn’t just simplify engineering, it grants engineers with new powers in the process.</p>
<p><strong>Transparency</strong></p>
<p>Ironically, with event-hidden engineering, every behavior of the application is now more visible in an actionable way because events are held in lower layers of the stack that understand their semantics and context.</p>
<p><strong>State Handling</strong></p>
<p>Events are not the only thing that get hidden.  By extension state handling, state changes and their implications for correctness are also buried in this new model.  The new developer experience is just implementing as if forever at “init time.”  All the progressions to get from 1 init time state to a future init time state are handled by the platforms powering the event-hidden architecture.</p>
<p><strong>Replayability</strong></p>
<p>Lastly, event-hidden architectures nearly always enable replayability.  Keeping events and state in a separate, well abstracted place makes it possible to give developers and operations teams a general way to time travel through the application’s computation.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://skiplabs.io/blog/event-hidden-arch#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<p>In 2025 it turns out you can go home again: you can build feature-rich, scaled, distributed applications without having to interact with events, queues and the industry of patterns, frameworks and consultants that came with them.</p>
<p>Event-hidden will become the default web app architecture for the next 10 years.</p>
<p><img decoding="async" loading="lazy" alt="Next ten years" src="https://skiplabs.io/assets/images/event-hidden-arch-next-ten-years-78dc2ca9ab2712500e8846371ee1355e.png" width="1600" height="800" class="img_ev3q"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[New built-in resources for Skip services]]></title>
            <link>https://skiplabs.io/blog/non-reactive-dependencies</link>
            <guid>https://skiplabs.io/blog/non-reactive-dependencies</guid>
            <pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How to make Postgres and Kafka-backed reactive services]]></description>
            <content:encoded><![CDATA[<p>The Skip framework is a system for building and running incremental backend services, simplifying the challenges of engineering complex reactive systems.</p>
<p>While Skip reactive services compose naturally with <em>each other</em>, they must also coexist with other backend systems with non-reactive semantics and APIs.</p>
<p>This blog post describes some recent enhancements we've made to the Skip framework to make it easy to bridge the reactive/non-reactive interface for popular systems like PostgreSQL and Kafka, and shows how you can build similar integrations with other systems and APIs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="postgres">Postgres<a href="https://skiplabs.io/blog/non-reactive-dependencies#postgres" class="hash-link" aria-label="Direct link to Postgres" title="Direct link to Postgres" translate="no">​</a></h2>
<p>PostgreSQL is one of the most popular relational databases in use today, serving as a highly scalable and resilient source-of-truth for critical application data.</p>
<p>For incremental computation, it can be tricky to maintain an up-to-date view of an application's state over a relational data model.
Different data sources add and modify values in an interconnected relational data model: you can either re-query and re-build the world periodically, or build some ad-hoc logic to propagate deltas.</p>
<p>Using Skip's <a href="https://skiplabs.io/docs/api/adapters/postgres/classes/PostgresExternalService" target="_blank" rel="noopener noreferrer" class="">Postgres adapter</a>, developers can define some computation over their Postgres state and use the Skip framework to watch for changes in the relevant tables and recompute only the minimal affected outputs.</p>
<p>To use the Postgres addapter, when defining your Skip service, specify one (or more) databases in the <code>externalServices</code> field:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> service </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  initialData</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  resources</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  createGraph</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  externalServices</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    postgres</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">PostgresExternalService</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> database</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Then, anywhere in that service, you can pull in a database table as a Skip collection by specifying a table name and a column to use as the key of the collection.
For example, given a PostgreSQL table <code>my_table</code> with schema</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> my_table </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">id </span><span class="token keyword" style="color:#00009f">SERIAL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">value</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TEXT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>we can use the table as a collection <code>myTable</code> in a Skip reactive service as follows.</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> myTable</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> id</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> value</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">useExternalResource</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    service</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"postgres"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    identifier</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"my_table"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    params</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> col</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"id"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"SERIAL"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Under the hood, Skip will maintain this collection up-to-date by watching the database for changes to <code>my_table</code>; the collection <code>myTable</code> will update incrementally the same as any other Skip collection and can be used in reactive computations combining multiple tables or any other Skip collection.</p>
<p>Although the <code>key</code> column specified here is the primary key column <code>id</code> of the table, it need not be a primary key or even unique -- it can be convenient to use a foreign key or other column to group multiple rows per key in the resulting Skip collection.</p>
<p>A more-involved example of a reactive service with computations over several Postgres tables is available <a href="https://github.com/SkipLabs/skip/tree/main/examples/hackernews" target="_blank" rel="noopener noreferrer" class="">here</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="kafka">Kafka<a href="https://skiplabs.io/blog/non-reactive-dependencies#kafka" class="hash-link" aria-label="Direct link to Kafka" title="Direct link to Kafka" translate="no">​</a></h2>
<p>Apache <a href="https://kafka.apache.org/" target="_blank" rel="noopener noreferrer" class="">Kafka</a> is another backend component with widespread adoption for high-throughput messaging and event streaming, often used to power real-time features.</p>
<p>Integrating a Kafka cluster with a Skip service is a natural way to build reactive logic on top of streaming data.
As such, we provide an ExternalService <a href="https://skiplabs.io/docs/api/adapters/kafka/classes/KafkaExternalService" target="_blank" rel="noopener noreferrer" class="">adapter</a> which handles the plumbing for you and abstracts Kafka message streams as Skip collections.</p>
<p>First, specify connection information and configuration in the <code>externalServices</code> field of your service:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> service </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  initialData</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  resources</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  createGraph</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  externalServices</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    kafka</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">KafkaExternalService</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> clientId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> brokers</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>then, consume messages into a reactive collection with a <code>usExternalResource</code> call, e.g.</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> myKafkaTopic</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> EagerCollection</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">useExternalResource</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    service</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"kafka"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    identifier</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"my-kafka-topic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    params</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>By default, messages are interpreted as their string key and value, but a "<code>messageProcessor</code>" can be provided to parse message payloads into other types, manipulate key structure, or pull in "topic" identifiers or other message metadata.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="other-external-apis">Other external APIs<a href="https://skiplabs.io/blog/non-reactive-dependencies#other-external-apis" class="hash-link" aria-label="Direct link to Other external APIs" title="Direct link to Other external APIs" translate="no">​</a></h2>
<p>Of course, there are many other technologies and systems and you may need to integrate those with Skip.
The simplest option for many non-reactive APIs is to periodically poll an HTTP endpoint; we provide a simple interface to specify a polled dependency on a non-reactive API:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> service </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  initialData</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  resources</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  createGraph</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  externalServices</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    myExternalService</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">PolledExternalService</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      my_resource</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// HTTP endpoint</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://api.example.com/my_resource"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// Polling interval, in milliseconds</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        interval</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// data processing into `Entry&lt;K, V&gt;[]` key/values structure</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function-variable function" style="color:#d73a49">conv</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Json</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token builtin">Array</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">from</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> k</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">k</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>This <a href="https://skiplabs.io/docs/api/helpers/classes/PolledExternalService" target="_blank" rel="noopener noreferrer" class=""><code>PolledExternalService</code></a> specifies a single endpoint polled every 5 seconds, but in general can include any number of endpoints with different polling intervals and converter/parser functions.</p>
<p>Of course, polling necessarily introduces some latency in the reactive service: data can become stale between polls.
On the other hand, increasing the request frequency can place undue load on the target non-reactive API.
As such, care should be taken when setting up a polled dependency to choose a reasonable interval, and where possible polling should be avoided in favor of genuinely reactive updates.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's next?<a href="https://skiplabs.io/blog/non-reactive-dependencies#whats-next" class="hash-link" aria-label="Direct link to What's next?" title="Direct link to What's next?" translate="no">​</a></h2>
<p>These three adapters (<a href="https://skiplabs.io/docs/api/adapters/postgres/classes/PostgresExternalService" target="_blank" rel="noopener noreferrer" class=""><code>PostgresExternalService</code></a>, <a href="https://skiplabs.io/docs/api/adapters/kafka/classes/KafkaExternalService" target="_blank" rel="noopener noreferrer" class=""><code>KafkaExternalService</code></a>, and <a href="https://skiplabs.io/docs/api/helpers/classes/PolledExternalService" target="_blank" rel="noopener noreferrer" class=""><code>PolledExternalService</code></a>) are provided to make it easy to integrate with common backend systems, but all three are built using only public APIs and can easily be extended or tweaked in user code.</p>
<p>If your application would benefit from additional adapters for other components and technologies, you can implement an <a href="https://skiplabs.io/docs/api/core/interfaces/ExternalService" target="_blank" rel="noopener noreferrer" class=""><code>ExternalService</code></a> to wrap non-reactive systems as inputs to your Skip service.</p>
<p>We welcome and support open-source contributions and feature requests and are always happy to answer questions or help out on Discord.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SkipLabs Funding]]></title>
            <link>https://skiplabs.io/blog/skiplabs-funding</link>
            <guid>https://skiplabs.io/blog/skiplabs-funding</guid>
            <pubDate>Tue, 25 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[SkipLabs raises $8M in seed funding]]></description>
            <content:encoded><![CDATA[<p><strong>The News</strong></p>
<p>We’re pleased to announce that SkipLabs has raised $8 million to bring incremental computing to applications everywhere.  We intend to use the entirety of this funding to enhance the open source Skip project and grow the developer community.</p>
<p>This financing also means we get to work with <a href="https://www.linkedin.com/in/lennypruss/" target="_blank" rel="noopener noreferrer" class="">Lenny Pruss</a> at Amplify Partners who have invested in some of today’s most prominent developer platforms such as dbt Labs, Prisma and Temporal to name a few and <a href="https://www.linkedin.com/in/alex-mackenzie-6aa80ab4/" target="_blank" rel="noopener noreferrer" class="">Alex Mackenzie</a> at Tapestry VC.  Check out Amplify’s <a href="https://www.amplifypartners.com/blog-posts/how-skiplabs-is-building-react-for-the-backend" target="_blank" rel="noopener noreferrer" class="">blog post</a> that shares their view on SkipLabs and the future of reactive systems and Tapestry’s <a href="https://www.tapestry.vc/perspectives/skiplabs-streaming-without-the-streams" target="_blank" rel="noopener noreferrer" class="">blog post</a> on why they chose to invest.</p>
<p>We are also humbled to earn the support of individual investors like Adam Gross, Spencer Kimball, Yann LeCun, Tom Occhino, Olivier Pomel and Nicolas Vasilache.</p>
<!-- -->
<p><strong>2025 so far…</strong></p>
<p>We launched Skip at the start of this year and are very happy with the reception we’ve received so far.  In the first two months since its alpha release, the Skip project has received more than 1,000 stars on github, more than 1,000 unique installations while our Discord community grew by 400%.</p>
<p>What has been most gratifying are the discussions working with users to apply Skip and incremental computing to making their backend applications fast, secure and easy to enhance.  Developers have used Skip to power real-time crypto trading applications, CRM content management systems and sports tournament management.</p>
<p>We look forward to hundreds more of these collaborations because the opportunity for incremental computing is everywhere, it just wasn’t easy enough to do before Skip.  Thanks to Skip, incremental computing will become the simplest way of building services that:</p>
<ul>
<li class="">Power real-time features</li>
<li class="">Source data from more than one place with more than one latency</li>
<li class="">Serve distinct views to diverse clients that are a combination of browser, mobile, agent and API</li>
<li class="">Move security and privacy implementations from the client to the server</li>
</ul>
<p><strong>The Road Ahead</strong></p>
<p>The Skip Framework is new, but it’s built on very mature technical foundations developed while we were at Meta.  We plan to incorporate feedback from the community for a few months more before we ship a beta release so we can be confident in our backward compatibility commitments.</p>
<p>In the meantime we have lots of exciting new features to ship.  In the past few weeks we’ve already added prebuilt external resources for Postgres and Kafka, a more efficient natively compiled runtime and native OAuth support.</p>
<p>In the months ahead we plan to add:</p>
<ul>
<li class="">Horizontal autoscaling</li>
<li class="">A built-in debugger that builds on Skip’s transactional heap to provide unprecedented visibility at runtime</li>
<li class="">Support for additional backend programming languages</li>
</ul>
<p><strong>Thank You</strong></p>
<p>Our deepest gratitude to our users, investors, advisors and early employees who have believed in and supported SkipLabs’ mission of incremental computing since the earliest days.  We’ll keep shipping and proving you were right all along.  Back to work!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why Skip?]]></title>
            <link>https://skiplabs.io/blog/why-skip</link>
            <guid>https://skiplabs.io/blog/why-skip</guid>
            <pubDate>Tue, 11 Feb 2025 12:00:01 GMT</pubDate>
            <description><![CDATA[Why use the Skip framework?]]></description>
            <content:encoded><![CDATA[<p><strong>Skip After Meta</strong>
image: /img/skip.png</p>
<p>Facebook allowed us to open source both Hack and Skiplang so we could share both with the larger engineering community.  I eventually decided to leave Facebook (now Meta) to build on the work of Hack and Skiplang and develop technologies that would address the same challenges we had at Facebook but for engineers generally.  To do this, SkipLabs needed to incorporate two new technologies to make the work we did at Facebook usable in a normal engineering context.</p>
<!-- -->
<ol>
<li class="">A way to manage objects transactionally as their state changes</li>
</ol>
<p>This is an essential and unique design choice in Skip’s approach to incremental computation.  Without it, developers have to program against streams and assume a large and insidiously subtle amount of complexity.  Incremental computation will only become mainstream if the dev and ops time experience is simpler and easier than the more common request/response paradigm, not just faster &amp; continuous.</p>
<p>Object handling needed to go beyond simple caching.  We needed an in-memory system of objects and collections that could maintain an up-to-date view of the data as it changed which we call **reactive collections**.</p>
<p>Most of the objects in a program can be managed with memoization: storing and reusing the results of expensive computations.  This can be fast, simple and efficient provided you know when to evict the memoized values.</p>
<p>In Skip, memoized objects are held in “Lazy Collections.” They compute and cache results only when needed clients and will automatically evict data from the cache to save memory.</p>
<p>Other objects serve as indices or dictionaries. These objects realistically cannot be memoized as every value in the dictionary needs to be up to date at all times.  Skip manages these objects as “Eager Collections,” keeping them in memory at all times and recomputing whenever a source updates.</p>
<p><img decoding="async" loading="lazy" alt="Skip runtime internals diagram" src="https://skiplabs.io/assets/images/why_skip_runtime_diagram-6fd1189100dc284f6230addeab291097.png" width="1999" height="976" class="img_ev3q"></p>
<ol start="2">
<li class="">An ability to program services that compute incrementally but in familiar languages.</li>
</ol>
<p>Skiplang was built from the ground up to support incremental computing and it’s very well suited to that purpose.  Realistically engineering teams outside of Meta have already picked their languages and stacks and it would be unrealistic to expect everyone to change to meet Skip.</p>
<p>We realized we could bring the most essential characteristics of Skiplang to already widely used languages, starting with TypeScript.  Skip aims to allow developers to implement programs declaratively while the framework automatically manages the state and decides what parts of the graph of computation need to be re-run.  For this to work, programs need to be deterministic, effect free and assume they are interacting with objects in a transactional heap.</p>
<p><img decoding="async" loading="lazy" alt="Skip runtime application stack" src="https://skiplabs.io/assets/images/why_skip_stack_diagram-188ec39fb0cadfa25f50309b93dad915.png" width="1999" height="1268" class="img_ev3q"></p>
<p>The Skip Runtime integrates with popular TypeScript runtimes (e.g. Node, Bun) and using proxy objects gives TypeScript a reactive, transactional heap.  We then added a TypeScript API that gives developers a simple programming model of map functions applied to the objects Skip is now holding in its transactional heap.  In this way, developers get to stick with their preferred language while Skip can transparently manage both the state of the objects and directs when different parts of a program are recomputed.</p>
<p>We added higher level features to the API to more easily manipulate those collections (filter, slice, join, etc ...), and added support for sub-collections (collections created while computing the entry of a collection), among other things.  In the future we think there’s an opportunity to add other (optional) higher level primitives to make building complex services even easier.</p>
<p>Today the Skip Framework exclusively supports TypeScript.  We plan to add support for Python and Java in the near future and other languages in the medium term.</p>
<p><strong>The Skip Framework</strong></p>
<p>By combining a programming model that enforced effect-free operations on immutable objects with a runtime that abstracted away state and data management details, Skip has grown from a language to a full framework that we’ve recently released.</p>
<p><strong>Why Skip is useful</strong></p>
<p>Skip can power services that perform any combination of reads and writes but the main benefits are experienced with reads.  Skip gives read-based features &amp; functions several advantages over the more typical request/response model.</p>
<p>Skip programs will:</p>
<p>Continuously react to changes in data and clients - by managing the state of objects and the impact of functions on those objects, Skip services can incrementally recompute and stream new values to clients without recomputing unchanged values unnecessarily.</p>
<p>Automatically manage their own state - a feature may need to fetch data from multiple sources, compute intermediate results in a specific order while hydrating &amp; invalidating different caches.  In Skip all of this complexity is handled by the framework.  Developers just write functions against collections for a single point in time.</p>
<p>Let you introspect their execution - reactive programs are historically challenging to inspect and debug.  Because the Skip runtime manages both the objects and their graph of computation, Skip programs are very easy to introspect.  We plan to add a debugger to the framework to capitalize on this part of the design.</p>
<p>Work alongside traditional request / response programs - Unlike past approaches to state management and reactivity, we wanted to make sure Skip was not an “all or nothing” engineering decision.  Vertically, Skip can run in one or several layers of an existing software stack. It can read and write data from preexisting REST services, streaming sources or some legacy backend.  Skip can also power a new service where it reads and writes from a traditional database.</p>
<p>Horizontally, Skip can power some or all the features of an application’s backend and coexists with other business logic written in other frameworks, languages, etc.  Within Skip’s context it's very easy to extend what you’ve implemented in a service through a feature called “mirroring” that lets a new service import and synchronize data from a pre-existing peer service.</p>
<p><strong>Why it feels awesome to use Skip</strong></p>
<p>Skip lets developers focus on the "what" instead of the "how." By defining the desired outcome declaratively, Skip generates the necessary logic to keep data live and reactive. It handles complexities like cache invalidation and recomputation efficiently without adding any cognitive burden for the developer.</p>
<p>We took a graph-based approach to the Skip programming model. You define your computation as a series of operations on collections, creating a **graph of reactive computations**. Each node in the graph can be a lazy or eager collection, and the framework automatically manages dependencies, updates, and cache invalidation for you.</p>
<p>Here’s the magic: You write your program as if **time is frozen**, defining static outputs in terms of static inputs.  Skip then turns this into a live, streaming system. If an input changes, the framework efficiently propagates updates to the affected parts of the graph, ensuring everything stays consistent. It even supports transactional updates, so you can modify multiple inputs simultaneously without breaking consistency.</p>
<p>This approach not only makes building incremental features easier but also scales better as the system grows. By focusing on what you want to build and letting Skip handle the rest, developers can deliver live, interactive features without the typical headaches.</p>
<p><strong>Why Skip matters</strong></p>
<p>Skip isn’t just about solving caching or problems of incremental computation, it’s about rethinking how we build systems that deal with constantly changing data. By giving developers tools to handle immutability, caching, and reactivity seamlessly, Skip makes it easier to write reliable, efficient, fast and scalable applications.</p>
<p>By combining the lessons learned from Facebook’s early challenges with the innovation of Skip, we’ve charted a path toward solving some of the most complex problems in real-time and reactive systems. This is just the beginning, and we’re excited to see where this journey takes us next.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Skip's Origins]]></title>
            <link>https://skiplabs.io/blog/skips-origins</link>
            <guid>https://skiplabs.io/blog/skips-origins</guid>
            <pubDate>Tue, 11 Feb 2025 12:00:00 GMT</pubDate>
            <description><![CDATA[Describe the origins of the Skip framework and programming language.]]></description>
            <content:encoded><![CDATA[<p><strong>Coping with success</strong></p>
<p>I joined a much-smaller-than-today’s Facebook in 2011 to work on what would eventually become the developer tools team. At the time I joined, the engineering team was in the hundreds and the tools we had for development were, well, not great. PHP was the backbone of almost everything, with millions of lines of code. JavaScript was secondary, and only a few critical services ran on other languages. The company was scaling rapidly, and our development practices weren’t keeping up.</p>
<p>As the codebase grew, so did the difficulty of maintaining and improving it.  Facebook became much more feature rich and simple enhancements became complicated and error prone.  Often new features needed to operate conditionally on how other preexisting features were already operating.  Essentially with each new layer of features teams added, state management became a greater concern.</p>
<!-- -->
<p><strong>Power of type systems</strong></p>
<p>Facebook’s codebase relied heavily on dynamic constructs called “gatekeepers” that determined which features were live for which users under different conditions. But these gatekeepers could change which code paths were executed in ways that weren’t always obvious during development.</p>
<p>The inability to recreate the state of a path made testing and refactoring a nightmare. Imagine this: you tweak a method, and three weeks later, you’re woken up at 3 AM because your change broke something deep in the application. Over time developers stopped touching critical parts of the codebase. Instead of refactoring and improving existing code, they’d build parallel versions—hacks on top of hacks. The technical debt was piling up, and we needed a way to make changes safely and confidently.</p>
<p>We believed we could use types to make deeper static analysis and automatic refactoring possible in the PHP language and this would help engineers bring some order to the chaos.  The system was called Hack. Adoption wasn’t immediate—there was some resistance—but over time, developers saw the value. We intentionally designed the type system to be more flexible than strictly “correct.” This decision allowed us to cover more of the codebase quickly, even if it meant making some trade-offs in precision. It worked. Hack became the default way to write PHP at Facebook, and it’s still in use today.</p>
<p><strong>Incrementality</strong></p>
<p>As Facebook applications grew denser and more layered, more teams were looking to add real-time features where users’ actions immediately impact others, like clicking a "Like" button and instantly updating the count across everyone’s apps.  Such features are essential because performance and interactivity grew user engagement. When an application feels fresh and interactive, it’s much more appealing than static content. However, implementing this in a scalable way is not straightforward.</p>
<p>A typical scenario: you fetch a Facebook post. Normally, the server retrieves data like the post’s text, the number of likes, and the comments. It applies rules to filter and prioritize what’s shown (e.g., highlighting the most relevant comments or enforcing privacy settings). This logic runs once and produces a static result—the post you see on your screen.</p>
<p><img decoding="async" loading="lazy" alt="Non-reactive request flow" src="https://skiplabs.io/assets/images/skips_origins_nonreactive_flow-27b5b4e943dfe82e44c7963cdcba989e.png" width="1468" height="572" class="img_ev3q"></p>
<p>Making this "live" means the post updates automatically. If someone likes it, the count changes instantly. If a comment gets edited or deleted, it reflects for everyone in real time. Achieving this requires "inverting the arrow."  Traditionally, a user’s actions—like adding a comment— produces a write to the database. To make things reactive, engineers needed to turn these writes into continuous streams of updates. For example:</p>
<p>1. <strong>User likes a post</strong> → Create a stream of updates for that post’s like count.
2. <strong>User adds a comment</strong> → Create a stream for the comments section.</p>
<p><img decoding="async" loading="lazy" alt="Reactive request flow" src="https://skiplabs.io/assets/images/skips_origins_reactive_flow-3e80732b4a2c80754a7c693c91fc5806.png" width="1468" height="572" class="img_ev3q"></p>
<p>Typically reactive frameworks compose these streams into live updates. While conceptually straightforward, this approach fell short because:</p>
<p><strong>Excessive recomputations</strong>: Each small change could require recalculating the entire post, which is expensive and inefficient.</p>
<p><strong>Caching problems</strong>: To compute incrementally but avoid excessive fetching and recomputation, you need caches. But managing caches as objects are mutated becomes a nightmare—how do you ensure they’re up-to-date without introducing inconsistencies?</p>
<p>Caches can work well for "hot" data—frequently accessed data with a short lifespan. But outside this narrow use case, caching comes with trade-offs:</p>
<p>1. <strong>Long TTL (Time-to-Live)</strong>: Keeps data in the cache longer, but risks showing stale or inconsistent data.
2. <strong>Manual Invalidation</strong>: Requires custom logic to invalidate caches when data changes. This is extremely error-prone and hard to maintain.</p>
<p>For large-scale systems like Facebook, neither approach was sustainable. We needed a better solution.</p>
<p>We created Skiplang to better serve our needs for better state management and real-time features &amp; services.  We believed we could use our experience developing type systems to flip the way we think about reactive systems. Instead of starting with database writes and building logic from there, Skiplang let you:</p>
<p>1. <strong>Define the Desired Output</strong>: Begin with what you want to build (e.g. a Facebook post).
2. <strong>Use Declarative Rules</strong>: Specify what data is needed and how it’s combined.</p>
<p>Skiplang was originally designed to handle incremental computation and caching, but its capabilities quickly expanded to address the needs of reactive features and efficient data handling.  We built Skiplang on the premise that <strong>cached objects were immutable</strong> - unchangeable once created. If we could guarantee that immutability, it would be easier to use static analysis of Skiplang’s typed language to track dependencies and know exactly when to invalidate the cache and where to recompute.  Traditional programming languages didn’t give us the tools to enforce these guarantees.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Skip alpha]]></title>
            <link>https://skiplabs.io/blog/skip-alpha</link>
            <guid>https://skiplabs.io/blog/skip-alpha</guid>
            <pubDate>Tue, 24 Dec 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Announce the alpha release of Skip]]></description>
            <content:encoded><![CDATA[<p>We’re pleased to share the alpha release of the <a href="https://github.com/SkipLabs/skip" target="_blank" rel="noopener noreferrer" class="">Skip Framework</a>, an open source (MIT license) system for building and running reactive backend services.</p>
<p>Skip gives TypeScript developers a simple declarative way to implement and run read-mostly features and services that are performant, transparent and continuously updated.
The framework handles all the complexities of state management, integrations and failure handling in the process.</p>
<!-- -->
<p>You can install via:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm i @skiplabs/skip</span><br></div></code></pre></div></div>
<p>This is an alpha release - we are happy with its stability and functionality but are looking forward to feedback on the interfaces and developer experience.</p>
<p>If you have time to check it out over the holidays and have feedback or just questions, <a href="https://discord.gg/4dMEBA46mE" target="_blank" rel="noopener noreferrer" class="">come talk to us in discord</a>.</p>
<p>Happy holidays,</p>
<p>The SkipLabs team</p>]]></content:encoded>
        </item>
    </channel>
</rss>