<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sean Travis Taylor</title>
    <description>The latest articles on DEV Community by Sean Travis Taylor (@agustus_gloop).</description>
    <link>https://dev.to/agustus_gloop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1882505%2F3f69d065-e2ea-4d37-8536-11e874018634.jpeg</url>
      <title>DEV Community: Sean Travis Taylor</title>
      <link>https://dev.to/agustus_gloop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/agustus_gloop"/>
    <language>en</language>
    <item>
      <title>So Your Boss Wants You to Use AI?</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Mon, 11 Aug 2025 16:27:30 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/so-your-boss-wants-you-to-use-ai-3db8</link>
      <guid>https://dev.to/agustus_gloop/so-your-boss-wants-you-to-use-ai-3db8</guid>
      <description>&lt;p&gt;Most bosses know just enough about AI to know that it &lt;em&gt;can&lt;/em&gt; have a transformative impact on their business. Often enough, they don't know &lt;em&gt;how&lt;/em&gt; and worse still they view AI as something like a "one weird rick", a magic spell or an efficiency hack.&lt;/p&gt;

&lt;p&gt;Armed with enough knowledge to be dangerous, leaders make all manner of demands on their team to "use AI", to "use all of our data" in order to do...&lt;em&gt;something&lt;/em&gt; which is never really clear.&lt;/p&gt;

&lt;p&gt;Watch out for words and phrases like: "integrate", "collaborate" or "work with AI" or "just put it into ChatGPT". These indicate profound naivety or plain willful ignorance when it comes to the complexities of using AI to create business value.&lt;/p&gt;

&lt;p&gt;Below are some ready replies to well-meaning but inane requests from bosses that allow you to look smart as an AI non-specialist. They may even help your team avoid a few footguns, timesinks and disaster projects in the process.&lt;/p&gt;

&lt;p&gt;Question: &lt;strong&gt;&lt;em&gt;Why don't we just put this into ChatGPT?&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Answer: &lt;em&gt;What exactly&lt;/em&gt; are we putting into ChatGPT? First off, I'm totally on board with using more generative AI in our workflows where that makes sense. To clarify: do you mean asking ChatGPT to create or prospect specific solution for us based on a particular prompt? Do you mean fine-tuning ChatGPT with prompting on some of our data? Can you provide a bit more color there?&lt;/p&gt;




&lt;p&gt;Question: &lt;strong&gt;&lt;em&gt;We have all this data. Why can't we use AI to [INSERT BUSINESS CAPABILITY OR FUNCTION]&lt;/em&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Answer: We certainly &lt;em&gt;can&lt;/em&gt; explore whether a Large Language Model &lt;em&gt;or another AI solution&lt;/em&gt; is appropriate for that use case. What data you feel is most pertinent to realizing that feature? Do we know &lt;em&gt;where&lt;/em&gt; that data &lt;em&gt;is&lt;/em&gt;? Is it in pristine Excel spreadsheets or is it spread across GDrive, random emails, Jira and Slack? Do we know what condition it's in? How much of the data is at the quality such that we could train a model for...what is it we're trying to accomplish again...?&lt;/p&gt;




&lt;p&gt;Question: &lt;em&gt;&lt;strong&gt;Can't ChatGPT already do this?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Answer: LLMs alone are &lt;em&gt;general&lt;/em&gt; tools with a lot of &lt;em&gt;general&lt;/em&gt; data about the world. The data that &lt;em&gt;we&lt;/em&gt; have here at BigCo. is not only proprietary it's highly &lt;em&gt;specific&lt;/em&gt;. No LLM, no matter how good it is, is going to have that high fidelity data. We could fine-tune ChatGPT or some other foundational model with our data to get quality outputs. Quick question: what makes you think an LLM or a chatbot is the right move here? What data from the business or our users or customers makes you believe this?&lt;/p&gt;




&lt;p&gt;Question: &lt;em&gt;&lt;strong&gt;I want everyone to think about how we can use AI to improve efficiency. How can we enhance the quality of operations with this technology?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Answer: Where do we think the greatest &lt;em&gt;business value&lt;/em&gt; will emerge from efficiency improvements? We could make a lot of efficiency improvements across teams and still not move the needle on our most important metrics. Can you be specific about &lt;em&gt;where&lt;/em&gt; in our company you see improved efficiency having &lt;em&gt;measurable&lt;/em&gt; impact? What data do we have about current operations to measure any AI-driven efficiency efforts against? &lt;/p&gt;




&lt;p&gt;Question: &lt;em&gt;&lt;strong&gt;I want to see a proof of concept for an "AI approach" to [INSERT BUSINESS PROBLEM]. I won't take 'no' for an answer. How do we get started? We'll probably need to hire some data scientists soon, right?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Answer: Let's first figure out what we're trying to achieve here. Do we want &lt;em&gt;predictions&lt;/em&gt;? Do we want &lt;em&gt;classifications&lt;/em&gt;? For example, using AI to do sentiment analysis on customer chats or feedback messages to give us a sense of how people are feeling about our service in real time? Once we settle on &lt;em&gt;that&lt;/em&gt;, let's find the most pertinent data and ensure that data is &lt;em&gt;clean, consistent and coherent.&lt;/em&gt; Then we can use something like &lt;a href="https://huggingface.co/autotrain" rel="noopener noreferrer"&gt;HuggingFace's AutoTrain&lt;/a&gt; feature to do some small experiments.&lt;/p&gt;




&lt;p&gt;This is obviously just a smattering of the uniformed and downright asinine directives workers can expect from bosses in the throes of AI fever. &lt;/p&gt;

&lt;p&gt;This technology isn't going anywhere, so we should absolutely embrace in the workplace. At the same time we need to ensure we remain results-focused, not matter what new tech comes down the pike and that above all we are honest with ourselves and our teams about its risks as well as its rewards.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>"Get More AI Skills," They Told Me</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Mon, 04 Aug 2025 16:08:13 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/get-more-ai-skills-they-told-me-15df</link>
      <guid>https://dev.to/agustus_gloop/get-more-ai-skills-they-told-me-15df</guid>
      <description>&lt;p&gt;No other advice is offered more to shell-shocked workers blitzed by the lattice of layoffs, the ghosted interviews and the &lt;em&gt;non-response&lt;/em&gt; responses than that of "focusing on AI skills".&lt;/p&gt;

&lt;p&gt;Workers are to believe that being "skilled at AI" will make them instantly more attractive candidates to companies. &lt;/p&gt;

&lt;p&gt;These are the same companies that, often enough, are rewarded by Wall Street for firing workers and pepper phrases like "LLM", "artificial intelligence" and "generative AI" into every earnings call, interview, think piece and forecast they create.&lt;/p&gt;

&lt;p&gt;Workers are to believe these same companies will pursue anyone "sufficiently skilled" rather those who are &lt;em&gt;most&lt;/em&gt; responsible for the creation of the top-tier AI models on the market. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The fiction of ‘sufficient skill’ masks the reality that AI’s biggest breakthroughs come from the few while the many are told to catch up&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If AI ends up being as transformational as its most ardent promoters claim, it is ridiculous to imagine that the same business leaders who have steered the steady cascade of layoffs over the past three years, in spite of record profits, will maintain their most consequential cost-centers rather than trim them in pursuit of improved margins.&lt;/p&gt;

&lt;p&gt;But let's assume this isn't true. &lt;/p&gt;

&lt;p&gt;Upskilling in AI as sound advice for today's working professional requires another assumption: that the business leaders calling for more AI skills are even arguing in good faith.&lt;/p&gt;

&lt;p&gt;Remember, the people peddling this advice are the same people overseeing the injection of generative AI into every conceivable corner of every major product we can name, &lt;em&gt;not&lt;/em&gt; because users and customers are clamoring for this and &lt;em&gt;not&lt;/em&gt; because it addresses stated user needs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generative AI isn’t answering demand, it’s manufacturing it. One product update at a time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The "upskilling on AI" trend is a necessary pre-condition for the mass sales and enterprise integration of AI "solutions". Without it, all the hawkers of such tools and services have is the biggest speculative bet in history, without any takers. Given the billions  that have been invested and the billions more &lt;em&gt;planned&lt;/em&gt; for AI, a narrative justifying all that spend is not only necessary but &lt;em&gt;required&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Anybody remember "learn to code"? This was only the most recent major upskilling effort workers were advised to undertake.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because software was eating the world and software-as-a-service was the knife and fork. We needed tons of engineering talent to build and scale massive SaaS platforms. The 2010s were the era when the biggest, most influential internet companies we can name were on the "come up".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In the 2010s, software ate the world and engineers set the table...&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that the platforms have been built and the key scaling challenges resolved, armies of engineers are no longer required to maintain these multi-billion dollar businesses.&lt;/p&gt;

&lt;p&gt;Now that the platforms have been built, where does all that engineering talent go?&lt;/p&gt;

&lt;p&gt;Now workers are to believe that AI will be different, that we'll need all these people to build AI systems and platforms in the same way we "&lt;em&gt;needed&lt;/em&gt;" all those engineers during the SaaS boom.&lt;/p&gt;

&lt;p&gt;But AI and associated disciplines like Machine Learning, Deep Learning and Data Science are specialist competencies. We'll need far fewer practitioners in order to make revolutionary advances that produce outsize value for companies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unlike the SaaS boom that required scaling teams of engineers, AI is a high-leverage domain where a small number of specialists can generate disproportionate value. This makes broad workforce demand unlikely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even if most business leaders understood AI as a precision solution to a well-defined business problem rather than an efficiency hack, where would they have the willing upskiller direct their attention? Building their own models from scratch? Mastering the intricacies of fine tuning the output of pre-trained models with LoRA? Building the data pipelines that feed machine learning models? Or do they simply mean better prompt engineering? &lt;/p&gt;

&lt;p&gt;Realistically how much of that fits into a single self-financed online course or certification program, what is the lifetime value of knowledge that has such a high degree of obsolescence and what are the competitive prospects of a holder of such certifications against a genuine specialist with a terminal degree? What really determines the amount of value either will add to a company, if any? &lt;/p&gt;

&lt;p&gt;These are the most important and sadly unanswered questions.&lt;/p&gt;

&lt;p&gt;Whatever else it may be, upskilling in AI will not be a vehicle for widespread or even modest employment any more than coding was before it. Cloud companies are designed for massive output relative to modest labor inputs; their ability to scale outputs infinitely is what drives their profitability. &lt;/p&gt;

&lt;p&gt;For all the noise around "upskilling in AI", we should expect the same employment boom and bust cycle from the last decade and prepare financially, professionally and emotionally for whatever comes after the AI hype.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>The Siren Song: Vibe Coding and the Build Trap</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Wed, 30 Jul 2025 18:02:55 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/the-siren-song-vibe-coding-and-the-build-trap-819</link>
      <guid>https://dev.to/agustus_gloop/the-siren-song-vibe-coding-and-the-build-trap-819</guid>
      <description>&lt;p&gt;The prospect of creating applications without the need of developers, designers or product managers is irresistible to anyone without the team or the technical knowledge or experience to build a modern software application. That is to say most people.&lt;/p&gt;

&lt;p&gt;We can and should encourage the use of vibe coding as another instrument in the rapid-prototyper's toolbox, since the promise of this practice is &lt;em&gt;not&lt;/em&gt; the creation of fully realized production apps, despite advertisements to the contrary, but to allow a person or a team to &lt;em&gt;validate&lt;/em&gt; product ideas and features at the speed of a prompt.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://t-ziegelbecker.medium.com/a-summary-of-escaping-the-build-trap-by-melissa-perri-d247f943a7b3" rel="noopener noreferrer"&gt;Build Trap&lt;/a&gt; exists when a single non-specialist can deploy an app for a few dollars after a frenzied 100 hours of vibe coding the same as it does when a full complement of designers, developers and testers spend a whole quarter to achieve the same.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Build Trap is what happens when we mistake output for outcome—&lt;strong&gt;when we build features or apps simply because we can, not because anyone needs them&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Any time we prioritize output over outcome, we are in danger of creating a product that nobody wants, nobody asked for and that nobody is interested in--a product that solves no problem of any great significance to anyone. &lt;/p&gt;

&lt;p&gt;Such products are, in a word, a waste.&lt;/p&gt;

&lt;p&gt;We have wasted time, energy and attention on a solution that appeals to our own sense of what meets the moment over what consumers indicate an urgent appetite for. &lt;/p&gt;

&lt;p&gt;We have wasted time, energy and attention on a course of action that will at best, do nothing and in the worst, actively destroy value: whether that's customer goodwill, earned trust or brand equity. &lt;/p&gt;

&lt;p&gt;Does the vibe-coded shotgun approach to production apps result in less waste? Absolutely. This is &lt;em&gt;not&lt;/em&gt; insignificant but it is hardly redemptive. Any time, energy and attention spent willing inadequate solutions into relevance is time gifted to our competitors and time for the market to move away from us.&lt;/p&gt;

&lt;p&gt;So what should we do? &lt;/p&gt;

&lt;p&gt;The good news is that it is the same as it ever was. Use whatever tools you have available (and generative AI is a hell of a tool) to create prototypes that you can get out quickly for the purpose of doing &lt;em&gt;experiments&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Rapid experimentation matters more than easy answers—&lt;strong&gt;because real value comes not from how fast we build, but from how fast we learn&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What should excite the non-specialist is not the time to &lt;em&gt;done&lt;/em&gt;, the time to a "finished" app but the time between experiments, the time between iterations: call it "cycle time".&lt;/p&gt;

&lt;p&gt;The lower our cycle-time, the faster we can conduct experiments and get useful feedback. This was &lt;em&gt;always&lt;/em&gt; the game. That a billion-dollar product and a pack of passionate users &lt;em&gt;resulted from the experimentation&lt;/em&gt; is a happy accident.&lt;/p&gt;

&lt;p&gt;Somewhere along the way we became obsessed with time-to-market as the defining element of product success, that is time-to-market with a product we can claim is "finished", instead of the arrival of a product that meets or exceeds customer expectations.&lt;/p&gt;

&lt;p&gt;Business stakeholders and non-specialists are excited by vibe coding because it seems to confirm their perpetual suspicion that all those developers and testers and designers were really just under-miners and obstructionists. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To the untrained, vibe coding doesn't reveal hidden efficiency—&lt;strong&gt;it confirms a long-held fantasy: that expertise was the bottleneck all along&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Finally," they exclaim. "Now we can just build!" &lt;/p&gt;

&lt;p&gt;And surely they can, but to believe software is the answer is to ask the wrong question. In their glee, the non-specialist forgets that software is &lt;em&gt;not&lt;/em&gt; the solution; it is the solution &lt;em&gt;expressed&lt;/em&gt; in code.&lt;/p&gt;

&lt;p&gt;Brilliant software inside a poor solution serves no one; this is hard to see and usually when we &lt;em&gt;do&lt;/em&gt; see it, it is because the claws of the Build Trap have forced us to do so.&lt;/p&gt;

&lt;p&gt;Vibe coding indeed opens new possibilities for creating code in greater quantity and speed than ever before--this is good--but given that, we should aim to be even wiser and act with even greater intention lest we succumb to its new perils, chief among them, the Build Trap.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Spec-Driven API Design with Artificial Intelligence</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Mon, 30 Jun 2025 18:20:35 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/spec-driven-api-design-with-artificial-intelligence-5416</link>
      <guid>https://dev.to/agustus_gloop/spec-driven-api-design-with-artificial-intelligence-5416</guid>
      <description>&lt;h2&gt;
  
  
  The AI Risk Matrix
&lt;/h2&gt;

&lt;p&gt;The greatest risk of AI-generated applications is their non-deterministic nature. The tendency to hallucinate means special care must be taken to validate LLM outputs.&lt;/p&gt;

&lt;p&gt;The greatest advantage of AI-generated applications is speed; they are trivial to create. This means we can move really fast.&lt;/p&gt;

&lt;p&gt;The greatest risk of existing tools is their rigidity; they struggle to adapt to emerging business requirements. They have to be fit for purpose and they must be maintained to stay useful and relevant.&lt;/p&gt;

&lt;p&gt;The greatest advantage of tools is their precision. Their fitness for purpose ensures deterministic outcomes as long as we use the tool and they have strong reliability guarantees when requirements remain consistent.&lt;/p&gt;

&lt;p&gt;The advantage of standards is wide interoperability, documentation and shared understanding. We save time by standing on the shoulders of giants.&lt;/p&gt;

&lt;p&gt;How can we combine the best of standards, AI-generated code and existing toolchains to build better software faster?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Thing That Gets You To The Thing
&lt;/h2&gt;

&lt;p&gt;Code generation has emerged as one of the key use cases of generative AI. Too much time is spent thinking about how to create apps from text-based prompts. This is at the expense of discussions about the potential of AI to build reliable and interoperable systems when used to create the low-level boilerplate code that forms the lion's share of so many codebases.&lt;/p&gt;

&lt;p&gt;Given LLMs propensity to hallucinate, we're required to create tooling and services that validate outputs. This is costly and perhaps even beside the point. Rather than use LLMs to generate apps from whole cloth, we should use them to build standards-compliant tools that can create deterministic outputs. These outputs can then be integrated into existing workflows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI isn't the thing. It's the thing that &lt;em&gt;gets&lt;/em&gt; you to the thing...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We must use AI-generated code not only to produce lines of code but code that can cooperate with legacy and emerging systems, can be understood by maintainers and avoids becoming another app for the slop heap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Standard
&lt;/h2&gt;

&lt;p&gt;Over the next few posts we'll be sharing insights from building a tool we call Foundry. &lt;/p&gt;

&lt;p&gt;Foundry is a CLI application that consumes an OpenAPI spec document and generates a working API service in a target language. The generated services can include a number of authentication schemes, middleware and validation mechanisms–all of which are specified within the source OpenAPI file.&lt;/p&gt;

&lt;p&gt;The standards-based approach is the key value proposition for Foundry: wherever a known standard exists, we use it. Why the obsessive focus on standards? &lt;/p&gt;

&lt;p&gt;Partly because we're lazy. When there is a sound proposed or fully published standard it means less work having to justify and document our approaches. It's one fewer design decision to make and guarantees that for the lifetime of our application there will be documentation and rich examples of our implementation whether we have the time or inclination to create our own documentation or not. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Be lazy, bro. Just use a standard..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A standards-centric also means others can create tools and services that connect and interoperate with our services without our explicit involvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Workhorse
&lt;/h2&gt;

&lt;p&gt;Lastly, we'll be using LLMs to create a lot of the boilerplate app generation code so we can focus on the best application design possible. Now that we can create code at the speed of a prompt, there is no reason to create well-crafted, standards-based code that's well-documented and delivers outsize value for end-users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The speed of generative AI allows every team to be design-first&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We'll also explore some insights on how to design codebases so that they are optimally suited to absorb AI-created code. It's imperative that we intentionally design our code so that we aren't plagued by the all-too common hallucinations and regressions that arise when using AI as a co-pilot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Our project will succeed if we use AI to create a tool much faster than we otherwise could have, that itself produces dependable standards-compliant API services every single time. Stay tuned. The journey is just beginning....&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop Hallucinating: APIs are the Missing Link For Reliable Agentic Applications</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Tue, 24 Dec 2024 16:46:01 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/stop-hallucinating-apis-are-the-missing-link-for-reliable-agentic-applications-3d5h</link>
      <guid>https://dev.to/agustus_gloop/stop-hallucinating-apis-are-the-missing-link-for-reliable-agentic-applications-3d5h</guid>
      <description>&lt;p&gt;In a previous &lt;a href="https://dev.to/agustus_gloop/apis-are-the-only-serious-approach-to-agentic-applications-1n7e"&gt;post&lt;/a&gt;, we discussed the challenges of building agentic applications solely with large-language models. Leaving aside the immense challenge of training large-language models on an infinite array of use cases in order to function as even a rudimentary personal assistant, we declared agentic applications built on the autonomous operation of UIs designed for humans as a wrong turn. &lt;/p&gt;

&lt;p&gt;Time and again, we hear &lt;strong&gt;reliability&lt;/strong&gt; is a chief obstacle preventing the serious implementation of LLMs and agentic applications in business systems across industries.&lt;/p&gt;

&lt;p&gt;A tool that behaves as expected only 7 out of 10 times is useless: whether it's a hammer, a light switch, an airplane or a toilet. How do we improve the reliability of a technology whose principal value propositions are: excellence at generating things (e.g. images, text, video) and with respect to text, sounding human-like?&lt;/p&gt;

&lt;h2&gt;
  
  
  A Tale of Two Computing Models
&lt;/h2&gt;

&lt;p&gt;Applications, as we know them, are evaluated on wholly different criteria--namely: consistency, specificity and determinism. The same input produces the same output forever. This is their competitive advantage.&lt;/p&gt;

&lt;p&gt;LLMs and other generative tools are a different story: identical inputs can produce mildly to wildly different outputs ranging from the amusing to the outright disturbing.&lt;/p&gt;

&lt;p&gt;We call divergence from an expected outcome a &lt;em&gt;bug&lt;/em&gt; in a traditional application and a &lt;em&gt;hallucination&lt;/em&gt; in an LLM. This difference is more than semantic; it reflects a fundamental conflict between operating paradigms that must be reconciled before agentic applications can succeed in mass adoption.&lt;/p&gt;

&lt;p&gt;Below we examine a novel approach to developing agentic applications by combining the best of these two powerful computing models; this solution ably competes with the current state of the art in AI agent development.&lt;/p&gt;

&lt;p&gt;Read on to see our take on the future of agentic applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  APIs and LLMs Compared
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;APIs&lt;/th&gt;
&lt;th&gt;LLMs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Highly structured (HTTP interface)&lt;/td&gt;
&lt;td&gt;Unstructured (natural language interface)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No tolerance for divergence&lt;/td&gt;
&lt;td&gt;High tolerance for divergence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Established means of integrating systems&lt;/td&gt;
&lt;td&gt;Novel means of integration systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does not adapt&lt;/td&gt;
&lt;td&gt;Highly adaptable (prone to hallucinate)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The relative assets and liabilities of APIs vs. LLMs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Above is a quick breakdown of the differences between APIs and LLMs. In 2024 business systems consist of sprawling APIs designed to be accessed over HTTP. By themselves and without modification, they are incompatible with natural language interfaces like an LLM.&lt;/p&gt;

&lt;p&gt;If we want such systems to be agentic, we have to retrofit a vector database onto them and do the hard work of vectorizing our data. &lt;/p&gt;

&lt;p&gt;This is all in service to making that data available in an implementation of RAG so our LLM can incorporate vast proprietary information into its responses. We may have to build a custom chatbot and we may still have to train our chatbot on our data.&lt;/p&gt;

&lt;p&gt;What an enormous investment to adapt our backend systems to what is, in essence, a new UI!&lt;/p&gt;

&lt;p&gt;We however, take a different approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;p&gt;It started with use cases. LLMs are exceptional at consuming textual input and sussing out an appropriate output. We figured if an LLM could do something as simple as deducing the relevant use case associated with a natural language request, we're most of the way toward a solution for a powerful AI assistant.&lt;/p&gt;

&lt;p&gt;A use case is anything we'd like the AI to accomplish on our behalf. We prototyped requesting a car with a ride-hailing app. We wanted to be able to speak into our phone's mic and have the assistant fetch us details on a ride somewhere, returning details like the cost and ride duration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Just What Are Your Intentions?
&lt;/h2&gt;

&lt;p&gt;We chose to use basic prompt engineering to train an LLM to create what we call an &lt;strong&gt;intent specification&lt;/strong&gt; from a request. For example, with a request like, &lt;em&gt;"Get me an Uber to Madison Square Garden,"&lt;/em&gt; the LLM will return something that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;bookmarked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;37.7752315&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-122.418075&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;bookmarked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;LAT&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;LNG&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AMC Empire 25&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Augustus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dd495dbb-5a2f-46ed-8009-ad2bf0b85fcc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.intents.mobility.get_ride&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;reply_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;903496b2-b3f2-4f70-8574-16ae38403550&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;From unstructured to structured. The LLM derives an intent from a natural language request. Our mobile app fashions the above &lt;strong&gt;intent spec&lt;/strong&gt; before sending it to our backend. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a template; as it is it is incomplete. However, with few-shot prompting, an LLM becomes very good at producing such templates. Now that we have an &lt;strong&gt;intent&lt;/strong&gt; that has been specified along with the destination name, our mobile app gathers the other contextual data required to query our backend, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;display name
&lt;/li&gt;
&lt;li&gt;userId
&lt;/li&gt;
&lt;li&gt;current location data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once our template is populated, we send the request to our backend. On our server, we use the &lt;strong&gt;Google Maps Geocoding API&lt;/strong&gt; to retrieve the coordinates for the destination parameter in our intent specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Driving It Home...
&lt;/h2&gt;

&lt;p&gt;Armed with these coordinates, we can make a call to the &lt;strong&gt;Uber API&lt;/strong&gt; to see what Uber services are available.&lt;/p&gt;

&lt;p&gt;When Uber replies, we construct an intent reply message and pass it back to our application. Our LLM knows how to convert an intent reply to natural language and from there it's a trivial matter to use a text-to-speech solution to give our app a voice.&lt;/p&gt;

&lt;p&gt;Et voila! We now have a voice-activated valet that we can instruct to fetch us a car using natural language.&lt;/p&gt;

&lt;p&gt;This design allows us to use structured data, which our existing APIs require, while also allowing us to explore the possibilities of natural language interfaces to provide our end users a richer experience interacting with our systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thinkin' About Tomorrow
&lt;/h2&gt;

&lt;p&gt;As the AI landscape continues to evolve, we will need as many strategies as possible for making the data locked away inside our systems accessible. We will want to do this with as little toil as possible. We may not have the resources or expertise in the form of AI or machine learning engineers to build bespoke agentic systems for us.&lt;/p&gt;

&lt;p&gt;The ability to leverage our existing engineering competencies toward emerging AI applications will be critical as this highly specialized knowledge will not exist in adequate supply.&lt;/p&gt;

&lt;p&gt;Finding ways to combine traditional APIs and advanced LLMs means we can not only get the best of both worlds when it comes to creating innovative solutions, it also ensures that, no matter what the future of LLMs holds, we will be ready to move at the speed of change.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>APIs Are the Only Serious Approach to Agentic Applications</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Tue, 10 Dec 2024 19:17:08 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/apis-are-the-only-serious-approach-to-agentic-applications-1n7e</link>
      <guid>https://dev.to/agustus_gloop/apis-are-the-only-serious-approach-to-agentic-applications-1n7e</guid>
      <description>&lt;p&gt;The explosion of generative AI and the budding experiments with agentic applications created a slew of products touting the ability to run our digital lives autonomously. Yet these products suffer from a common flaw: they are designed to operate user interfaces that are themselves designed to be operated by human beings.&lt;/p&gt;

&lt;p&gt;The makers of these products congratulate themselves on the ability of their offerings to use web browsers, fill out UI forms, navigate web pages and follow links.&lt;/p&gt;

&lt;p&gt;This is waste: an AI agent without eyes doesn't need a UI and given the increasing cost in dollars and kilowatts required to run generative AI applications, we’re bound to see more waste as these products and services gain mass adoption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking the Scenic Route
&lt;/h2&gt;

&lt;p&gt;The result? AI agents will be slower given the need for a UI layer. Further, AI agents will run into the same obstacles human users do when using a typical web application: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;counterintuitive UI design &lt;/li&gt;
&lt;li&gt;confusing UI flows &lt;/li&gt;
&lt;li&gt;lack of accessibility&lt;/li&gt;
&lt;li&gt;broken web apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agents will be slower given the need to contact a command-and-control process to issue instructions to a peer process driving a web browser, which then completes—or fails to complete—a user request, which then sends results &lt;em&gt;back&lt;/em&gt; to the command-and-control process, which then digests the result and finally informs a user.&lt;/p&gt;

&lt;p&gt;This is unjustifiably wasteful irrespective of implications for increased emissions and overconsumption of electricity–to say nothing of the financial cost of a request to an AI application relative to a RESTful API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Dilemma
&lt;/h2&gt;

&lt;p&gt;Why build this way? Why the curious obsession with technology that accomplishes human goals with superior results but always, as the song goes: &lt;em&gt;like humans do&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Solutions in AI or robotics that garner the most praise or elicit the most fear are those that mimic human behavior: humanoid robots that work in factories or AI that operates your desktop computer.&lt;/p&gt;

&lt;p&gt;These innovations evoke such fear because of a flaw in design philosophy apparent to anyone with eyes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Robot Dreams
&lt;/h2&gt;

&lt;p&gt;These innovations are designed to replace human beings.&lt;/p&gt;

&lt;p&gt;This leads to a troubling design approach. When the frame of reference for any AI solution is that of human beings and how &lt;em&gt;they&lt;/em&gt; might accomplish a task, the result is androids and other Asimovian robot dreams.&lt;/p&gt;

&lt;p&gt;Never mind that humans and their bodies make poor machines, both in practical and philosophical terms. If the design prompt, or the assumption within it, is to make a human but better, the solution will prioritize human mimicry over every other criterion.&lt;/p&gt;

&lt;p&gt;I spent the summers of my college years working at a Toyota plant in my home state of Kentucky. It was a dazzling operation combining both human and machine innovation. Industrial robots abounded yet none of them looked like a human being.&lt;/p&gt;

&lt;p&gt;And why should they?&lt;/p&gt;

&lt;p&gt;Automated stamping of car bodies in the most efficient way requires unique capacities and even a super humanoid robot makes less sense than the industrial robots in use today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human Machines | Machine Humans
&lt;/h2&gt;

&lt;p&gt;Human bodies evolved in response to unique environmental requirements irrelevant in modern industrial contexts. In such contexts, what is required is a solution sensitive to the operational challenges in question, not a superhuman substitute.&lt;/p&gt;

&lt;p&gt;The similarities to agentic application design are striking. We see the same approach centering human mimicry: an agent autonomously clicks and navigates through your web browser or desktop UI, doing your tasks largely in the way &lt;em&gt;you yourself would do them&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Humanoid robots and AI-enabled self-driving user interfaces may inspire fear or awe but if the aim is achieving the objective in the most effective efficient way possible, attempts like these will routinely fail. &lt;/p&gt;

&lt;p&gt;The extraordinary cost to maintain AI applications demands design approaches that leverage the strength of the underlying technology and avoid the urge to mimic human UI interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternative Approaches
&lt;/h2&gt;

&lt;p&gt;What is an alternative?&lt;/p&gt;

&lt;p&gt;It involves using LLMs (large language models) to do what they excel at: extracting user intent from natural language. Traditional APIs, on the other hand, excel at taking structured data as a request and executing business logic in response. By combining these strengths, we can build efficient and effective AI applications that avoid unnecessary complexity and waste.&lt;/p&gt;

&lt;p&gt;In the next &lt;a href="https://dev.to/agustus_gloop/stop-hallucinating-apis-are-the-missing-link-for-reliable-agentic-applications-3d5h"&gt;post&lt;/a&gt;, we'll explore a real-world example app where, instead of using massive sets of training data, I use basic prompt engineering and a simple REST API to create a natural language Uber app. &lt;/p&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bussin': Database as a Message Bus</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Tue, 03 Dec 2024 15:45:09 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/bussin-database-as-a-message-bus-454p</link>
      <guid>https://dev.to/agustus_gloop/bussin-database-as-a-message-bus-454p</guid>
      <description>&lt;p&gt;A recent &lt;a href="https://dev.to/agustus_gloop/for-the-love-of-godjust-use-supabase-8oa"&gt;post&lt;/a&gt; extolled the virtues of exploring reactive databases for decoupled applications. Why is loose coupling so desirable? &lt;/p&gt;

&lt;p&gt;If you've been reading this blog for a while, you know the answer. &lt;/p&gt;

&lt;p&gt;Decoupled applications allow us to embrace change. When we design for change every architectural decision is less consequential. It means we can reduce the switching costs associated with any given approach. &lt;/p&gt;

&lt;p&gt;We also get the gift of freedom–to experiment, to take measured risks and to innovate.&lt;/p&gt;

&lt;p&gt;We know we want modifiable applications. We also know we want to decouple the implementation details of communication between parts of our application. That is to say we are less concerned with whether, for example, we communicate via RESTful API or GraphQL or message brokers. &lt;/p&gt;

&lt;p&gt;Instead we concentrate on the &lt;em&gt;meaning&lt;/em&gt; of the messages we exchange among our services.&lt;/p&gt;

&lt;p&gt;This week’s pattern builds on using the Publish/Subscribe capabilities of reactive databases to create a message bus. We'll see how this approach allows seamless communication between services and affords easy extensibility of the application when it's time to introduce new features. &lt;/p&gt;

&lt;p&gt;Read on to learn more about the promise and perils of taking the bus.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bus? Seriously…?
&lt;/h2&gt;

&lt;p&gt;At its base, a message bus is a central communication link that services can use to listen for and dispatch messages of interest to anyone or anything connected to the bus. Adding or removing capabilities is simple: either case requires only attaching or detaching from the bus. Take a look. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5h2cukmwewlb1m0auf59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5h2cukmwewlb1m0auf59.png" alt="Image description" width="800" height="966"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Set it and forget it. Application services can connect and disconnect whenever they like. Communicating with event messages offers unparalleled flexibility.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Services are unaware of what peer services are connected to the bus. They are only able to post messages of interest &lt;em&gt;to&lt;/em&gt; the bus.&lt;/p&gt;

&lt;p&gt;This pattern shares similarities with the classic Publish/Subscribe pattern, where each service registers its own listeners. A challenge with this approach is that it's difficult to manage all  publishers and subscribers as the system grows.&lt;/p&gt;

&lt;p&gt;A message bus, similar to a message broker, can centralize operational logic like retries, routing policies, error handling and other concerns that would be duplicated in an architecture with a host of point-to-point connections between publishers and subscribers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Ride Down Memory Lane…
&lt;/h2&gt;

&lt;p&gt;If this sounds reminiscent of the centralized enterprise service buses of the 90s, you've got a good memory. This architectural approach was the foundation of what became known as service-oriented architecture or SOA.&lt;/p&gt;

&lt;p&gt;This paradigm fell out of favor as more logic concentrated in the message bus, creating challenges as the system scaled in terms of connected services. In fact, it created exactly the type of obstacles such a decoupled architecture is intended to avoid with the message bus as a single point of failure.&lt;/p&gt;

&lt;p&gt;Again, self-describing messages offer an innovative solution. The core problem with the Enterprise Service Bus is the accumulation of message processing logic within the bus itself, as well as the coordination of message schemas among connected services.&lt;/p&gt;

&lt;p&gt;By removing the logic for managing messages from the bus and embedding data required for message processing within the messages themselves, our bus is &lt;strong&gt;solely&lt;/strong&gt; concerned with carrying messages.&lt;/p&gt;

&lt;p&gt;When we eliminate logic from the bus and store all application state and ancillary processing data inside the messages, the result is a near-stateless application.&lt;/p&gt;

&lt;p&gt;In the example application below, notice that there is no persistence layer the application uses to do its work aside from publishing events and responding to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking the Bus for Ice Cream
&lt;/h2&gt;

&lt;p&gt;Our demo app is an ice cream service. It receives orders for customers, charges the customer card and fulfills the order provided inventory is available. &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/js-7vybaf?embed=1&amp;amp;file=index.js&amp;amp;view=editor" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; as our database of choice because...&lt;a href="https://dev.to/agustus_gloop/for-the-love-of-godjust-use-supabase-8oa"&gt;duh&lt;/a&gt;; this spares us having to create an API surface just to test our app. Since our &lt;code&gt;FOHService&lt;/code&gt; (which could be anything including a UI app) is listening for inserts into the &lt;code&gt;events&lt;/code&gt; table, it means we have a full stack app with a REST API in seconds.&lt;/p&gt;

&lt;p&gt;The rest of the application is driven by the flow of events.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/" rel="noopener noreferrer"&gt;Stackblitz&lt;/a&gt; is our backend in this case. Here we define our services and their subscriptions and we publish relevant events. The implementation of this pattern (the &lt;em&gt;how&lt;/em&gt;) is less important than &lt;em&gt;what&lt;/em&gt; is accomplished:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the app completes tasks with refreshingly little state to manage&lt;/li&gt;
&lt;li&gt;decoupled services are easy to replace, rollback or trial&lt;/li&gt;
&lt;li&gt;if we know how to create &lt;a href="https://dev.to/agustus_gloop/bulletproof-backends-a-strategy-for-adaptive-services-49d3"&gt;self-describing messages&lt;/a&gt;, message structure can evolve without disrupting peer services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Toward Stateless Services
&lt;/h2&gt;

&lt;p&gt;This approach of putting all stateful information into streams of successive messages is known as &lt;a href="https://martinfowler.com/articles/201701-event-driven.html" rel="noopener noreferrer"&gt;Event-Carried State Transfer&lt;/a&gt;. By carrying the relevant state in the message itself, downstream services don't need to query other systems for additional context.&lt;/p&gt;

&lt;p&gt;As in our previous exploration of self-describing messages, they once more prove an invaluable contribution to system adaptability.&lt;/p&gt;

&lt;p&gt;As in earlier examples with self-describing messages, our connected services can evolve their message schemas without disrupting the message processing of their peer services.&lt;/p&gt;

&lt;p&gt;Finally, as in previous posts, we see another pattern that demonstrates the possibilities of architectures that reduce the cost of experimentation, increase the freedom to innovate, and above all, allow us to author systems that can move at the speed of change.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bulletproof Backends: A Strategy for Adaptive Services</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Wed, 20 Nov 2024 19:08:24 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/bulletproof-backends-a-strategy-for-adaptive-services-49d3</link>
      <guid>https://dev.to/agustus_gloop/bulletproof-backends-a-strategy-for-adaptive-services-49d3</guid>
      <description>&lt;p&gt;This post is a digest of a conference talk I gave at the Nordic API Summit in Stockholm, Sweden. For those who prefer video to the written word, here's a &lt;a href="https://youtu.be/nvkErhiOtTM?si=HpknyNXUkh19I8bH" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a deeper dive, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building for Change
&lt;/h2&gt;

&lt;p&gt;Much of the focus of this blog is creating services that can absorb change. Why? Because change is the most reliable business condition under which software must operate.&lt;/p&gt;

&lt;p&gt;When we create systems that are amenable to change, it means we can experiment. We get to try lots of solutions to our problem. The cost of implementing any set of changes is decreased because our systems are not designed like Fabergé eggs.&lt;/p&gt;

&lt;p&gt;New features and capabilities are easier to add without re-architecting the whole system or significant parts of it.&lt;/p&gt;

&lt;p&gt;All of this comes down to reduced costs: a system that is less costly to change, to build, and to maintain. It is important to note however, that we speak not only of costs that can be strictly quantified. &lt;/p&gt;

&lt;p&gt;We should also include reduced costs of debugging and reduced costs of our engineering organization's time, energy, and attention. Such cost reductions are absolutely worth taking a look at the pattern below.&lt;/p&gt;

&lt;h2&gt;
  
  
  State of the Art
&lt;/h2&gt;

&lt;p&gt;If you design most API integration solutions like most companies, you are probably binding directly to the internal data model of a third-party service over which you exercise no control. &lt;/p&gt;

&lt;p&gt;It looks like the following. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlib3wiph0p4iisvgi5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlib3wiph0p4iisvgi5x.png" alt="Image description" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Directly binding to internal data models is most unwise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This approach is by far the most intuitive design and is effective as far as it goes. &lt;/p&gt;

&lt;p&gt;It is also brittle. &lt;/p&gt;

&lt;p&gt;As soon as the services change at all, everything falls apart. The only way to resolve the broken system is a manual reconciliation—that is, a meeting between teams. Meanwhile precious time and, depending on the system, money is wasted.&lt;/p&gt;

&lt;p&gt;This cycle repeats for as long as the services are in operation. &lt;/p&gt;

&lt;p&gt;This is the state of the art in 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acceptance Criteria
&lt;/h2&gt;

&lt;p&gt;How can we break the cycle? How can we create services that do not directly bind to the internals of peer services?&lt;/p&gt;

&lt;p&gt;We will need two key ingredients: JSON Patch and self-describing messages.&lt;/p&gt;

&lt;p&gt;Once we learn how to apply these ingredients, we will have everything we need to create services that exchange messages without needing to bind directly to the implementation details of peer services.&lt;/p&gt;

&lt;p&gt;First things first: the core of the integration problem is the need for services to agree &lt;em&gt;exactly&lt;/em&gt; on the details of message structure before they can communicate. This means as soon as the message structure diverges, the communication link is broken.&lt;/p&gt;

&lt;p&gt;What would help is a way to translate one object structure to another on the fly. JSON Patch offers a solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Patch
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://jsonpatch.com" rel="noopener noreferrer"&gt;JSON Patch&lt;/a&gt; is a formal specification for applying a series of transformations to a candidate object to produce a result object. &lt;/p&gt;

&lt;p&gt;Since a JSON Patch document is just a JSON object, it can be serialized. Because it can be serialized, it can be sent over the wire. Because it can be sent over the wire, we can create a link to it. But what do links have to do with anything?&lt;/p&gt;

&lt;p&gt;To understand, we need to discuss self-describing messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-describing Messages
&lt;/h3&gt;

&lt;p&gt;What &lt;em&gt;is&lt;/em&gt; a self-describing message? It is a message that contains all the information required to process the message &lt;em&gt;within&lt;/em&gt; the message itself.&lt;/p&gt;

&lt;p&gt;If you're looking for examples, look no further than the webpage you're viewing right now. Everything required to render the page successfully is embedded in the HTML: from fonts to stylesheets to third-party scripts, images, video, etc.&lt;/p&gt;

&lt;p&gt;When the page updates its CSS, you don't need to update your &lt;em&gt;browser&lt;/em&gt;. No. You get a link to a stylesheet that contains the updated style code. Your browser is none the wiser. Another example: Have you ever received a package from FedEx, UPS, or DHL?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrhb4tt3iuw7z7wlq718.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrhb4tt3iuw7z7wlq718.jpg" alt="Image description" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Packages are great examples of self-describing messages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It probably looks something like the image above.&lt;/p&gt;

&lt;p&gt;What is significant about it? If you have ever received a package—certainly any international parcel—you will notice all kinds of metadata (i.e. labels) on the box.&lt;/p&gt;

&lt;p&gt;These labels tell processors how to process the package, which is the message in this example. Is the package fragile? Is it perishable? Does the package contain hazardous materials? Is the package accompanied by customs paperwork that offers hints about how to further process it?&lt;/p&gt;

&lt;p&gt;What about tracking labels? &lt;/p&gt;

&lt;p&gt;These are links to an outside system. They allow us to query a third-party data store for additional information about where the package has been and where it is going.&lt;/p&gt;

&lt;p&gt;We can glean all of this information without needing to know what's inside the package. This is the essence of the self-describing message.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Adapter Schema
&lt;/h2&gt;

&lt;p&gt;The last thing we need is something called an Adapter Schema. This schema is one that &lt;strong&gt;neither&lt;/strong&gt; of our collaborating services depend on for their business logic. This schema is &lt;strong&gt;only&lt;/strong&gt; used to adapt one internal format to another while retaining all the fields of interest. Let's take a look at an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Foo Service&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;Jane&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;Doe&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Adapter Schema&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;Jane&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;Doe&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Bar Service&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;given_name&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;Jane&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;surname&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;Doe&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;When we focus on the &lt;em&gt;meaning&lt;/em&gt; of messages rather than their structure, we give ourselves the freedom to iterate on structure as needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Clearly, the above three objects refer to the same bit of information and that information has equivalent meaning. Structurally they differ but semantically they are the same. &lt;/p&gt;

&lt;p&gt;The second object is our adapter schema and the first and third objects represent the data models of two distinct services. Our cooperating services will use this adapter to capture the meaningful data fields within the messages they exchange.&lt;/p&gt;

&lt;p&gt;In the adapter schema we have flattened everything to the top level of the object but we needn't do so. The adapter schema can be as complex as our needs require. The key is that this schema is the only information &lt;em&gt;that needs to be agreed upon ahead of time&lt;/em&gt; by our collaborating services.&lt;/p&gt;

&lt;h1&gt;
  
  
  A Solution Emerges
&lt;/h1&gt;

&lt;p&gt;Once we have our adapter schema, we simply need to send a link to the patch document that will allow consumers of our messages to translate them to the adapter schema. From there, the consuming service can do any needed additional translations from the reliably stable adapter schema to the consuming service's own internal model.&lt;/p&gt;

&lt;p&gt;Above, Foo Service can provide a link to a JSON Patch document that will allow Bar Service to translate from Foo’s internal model to the Adapter Schema. That patch document looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt; 
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;op&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;move&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/name/first&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/firstName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;op&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;move&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/name/last&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/lastName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;op&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remove&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Recipe for transformation: a JSON Patch document is a set of instructions for moving from one object shape to another&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As an alternative, services can exchange JSON Patch links that allow message consumers to translate directly to internal object models. &lt;/p&gt;

&lt;p&gt;Regardless of approach, so long as the adapter schema is stable and each of our outgoing messages contains the link to the translating JSON Patch document, our services &lt;em&gt;will not&lt;/em&gt; break owing to structural changes to our messages.&lt;/p&gt;

&lt;p&gt;Message consumers are free to change their internal structures as much as they want without fear of breaking the integration. A reply message may also contain a link for a requesting service to translate back to the adapter schema before further processing of the reply.&lt;/p&gt;

&lt;p&gt;In this way, all of our cooperating services can modify their internal object model as much as they like while still retaining the value of integration, the possibility of innovation, and most importantly, the ability to move at the speed of change.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>For the Love of God...just use Supabase</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Tue, 12 Nov 2024 16:43:06 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/for-the-love-of-godjust-use-supabase-8oa</link>
      <guid>https://dev.to/agustus_gloop/for-the-love-of-godjust-use-supabase-8oa</guid>
      <description>&lt;p&gt;I will make a bold claim: there is no reason to build CRUD APIs anymore. You know the kind. API endpoints that just ferry JSON objects into a database to make them queryable by something like &lt;code&gt;/ice-creams/{id}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Perhaps it's a SQL database. Maybe it's MongoDB. It doesn't matter. Unless you are transforming the incoming request payload, and sometimes not even then, there are few reasons to handwrite API endpoints in 2024. &lt;/p&gt;

&lt;p&gt;Honestly, there weren't many reasons to do it in 2017 either.&lt;/p&gt;

&lt;p&gt;A few years ago, I was in an interview for a senior back-end software engineering role. The interviewer asked how I would build the back-end for a real-time collaborative word processing app like Google Docs.&lt;/p&gt;

&lt;p&gt;"Well, I would start with a reactive database–" I began.&lt;/p&gt;

&lt;p&gt;"Well, let's assume we don't have one of those," she said, steering the interview down a different path.&lt;/p&gt;

&lt;p&gt;Eventually, we did talk our way through an architecture that satisfied her and I did end up landing the job. Yet I never forgot this experience because it describes a common approach to developing backend systems then and now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current State
&lt;/h2&gt;

&lt;p&gt;You've been in meetings that center on building this way. Maybe it's a sprint planning meeting or a call between technical and non-technical business stakeholders. It goes something like this:&lt;/p&gt;

&lt;p&gt;"Well, first we need to create a new table in the database."&lt;/p&gt;

&lt;p&gt;"Then we'll need to create an endpoint."&lt;/p&gt;

&lt;p&gt;"Then we'll need to wire the two together."&lt;/p&gt;

&lt;p&gt;"Given what we've got in our backlog already, maybe we can get to this in two or maybe three sprints."&lt;/p&gt;

&lt;p&gt;God help you if the endpoint in question has any non-trivial business logic in it.&lt;/p&gt;

&lt;p&gt;You'll watch helplessly as the runtime of your meeting grows, threatening to crash into...your next meeting.&lt;/p&gt;

&lt;p&gt;If you and your team are changing an existing endpoint, the drama above still unfolds unchanged.&lt;/p&gt;

&lt;p&gt;There are good reasons that there's often no escape from this timeless way of building. When reactive databases didn't exist, the three-tier architecture—that is, separating a backend into an API interface, a logic layer, and a persistence layer—made a lot of sense. It allows you to separate the concerns of your application while also providing flexibility for each component in the architecture.&lt;/p&gt;

&lt;p&gt;This is a good thing.&lt;/p&gt;

&lt;p&gt;But as database solutions matured they also became more convenient. We saw a decrease in the cost of change, which makes re-examining these solutions worthwhile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future State
&lt;/h2&gt;

&lt;p&gt;Instead of using a traditional three-tiered architecture, you might use SupaBase or its lightweight cousin PocketBase, both of which provide a REST-like API interface for all your tables and collections out of the box.&lt;/p&gt;

&lt;p&gt;If you need to perform business logic on incoming requests, maybe you subscribe to changes on a specific table in PocketBase or SupaBase and transform requests within that subscription, then commit the result of the transformation to the same table or an entirely new one.&lt;/p&gt;

&lt;p&gt;If you worry about vendor lock-in, maybe you use a vanilla Postgres database and layer PostgREST on top of it to provide a rich REST-like API interface for all of your SQL tables.&lt;/p&gt;

&lt;p&gt;Are there challenges with this architectural approach? Most certainly. &lt;/p&gt;

&lt;p&gt;You may be uncomfortable with how this architecture gently pushes you toward an asynchronous message flow rather than request-reply, which is far more straightforward. This can be a paradigm shift that many teams are unprepared for. They may also lack the institutional support from leadership to take even calculated risks with this approach.&lt;/p&gt;

&lt;p&gt;Having pieces of business logic spread among many subscriptions and publishers can create code management, organization, and debugging challenges. &lt;/p&gt;

&lt;p&gt;Your DevOps or SecOps stakeholders may resist the idea of the database being directly exposed to the public internet this way. While Postgres' Row-Level Security features offer fine-grained control over visibility and access to the database rows and tables, there may be other compelling reasons why this architecture is unsuitable for your organization. It may require a complete reimagining of existing infrastructure components.&lt;/p&gt;

&lt;p&gt;Despite these caveats, exploring today's current spate of reactive databases complete with REST APIs out of the box is a business imperative in any organization requiring agility in the face of constant change.&lt;/p&gt;

&lt;p&gt;Helpful hint: That's every single organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Help Is On the Way
&lt;/h2&gt;

&lt;p&gt;Below, I outline four database solutions that either allow you to transform an existing SQL database into an API or provide a database and an API custom fit for your tables or document collections. Take a look.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; is a full-fledged backend-as-a-service built on PostgreSQL that offers a real-time database, authentication, storage, and API services. It automatically generates RESTful APIs based on your database schema, and integrates smoothly with PostgreSQL’s robust querying capabilities. Supabase is particularly beneficial for developers who want a quick setup with minimal configuration, but need the flexibility and power of PostgreSQL, plus real-time functionality and easy integrations for modern web and mobile applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pocketbase.io/" rel="noopener noreferrer"&gt;Pocketbase&lt;/a&gt; is a lightweight, open-source backend solution that combines a real-time database with file storage and authentication services. Its key benefits include simplicity and portability, as it can be run locally or in the cloud without much overhead. Designed to be user-friendly for both small projects and rapid prototyping, Pocketbase makes it easy for developers to quickly deploy applications with built-in user authentication and admin UI for managing data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.postgrest.org/en/v12/index.html" rel="noopener noreferrer"&gt;PostgREST&lt;/a&gt; generates RESTful APIs directly from a PostgreSQL database, eliminating the need for an intermediary API server. Its key benefits lie in the simplicity of generating fully-featured, highly-performant APIs from the database schema itself, leveraging PostgreSQL’s robust permissions system and allowing developers to focus on database logic, rather than writing API endpoints. This ensures efficient, scalable solutions with minimal overhead for API management.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.dreamfactory.com/" rel="noopener noreferrer"&gt;DreamFactory&lt;/a&gt; is an open-source REST API generator that can automatically create APIs for any database, including SQL, NoSQL or other services. Its benefits include support for multiple databases and third-party integrations, automatic API documentation and flexible API customization options. DreamFactory simplifies the process of creating secure, scalable APIs without requiring manual coding, while supporting authentication, role-based access control and real-time data streaming for diverse backend infrastructures.&lt;/p&gt;

&lt;p&gt;I hope you’ll explore some of the solutions outlined above in your next prototypes to see if you’re finally ready to stop building APIs like it’s 1999. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Back to the Sandbox: Leveling up with Lazy Loading and Dependency Injection</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Wed, 23 Oct 2024 15:12:28 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/back-to-the-sandbox-leveling-up-with-lazy-loading-and-dependency-injection-2cpj</link>
      <guid>https://dev.to/agustus_gloop/back-to-the-sandbox-leveling-up-with-lazy-loading-and-dependency-injection-2cpj</guid>
      <description>&lt;p&gt;In the last &lt;a href="https://dev.to/agustus_gloop/jump-in-sandboxes-for-decoupled-applications-77j"&gt;post&lt;/a&gt;, we explored the Sandbox pattern for decoupled applications that we can easily modify and extend as business needs change and as new requirements emerge. &lt;/p&gt;

&lt;p&gt;We led with the benefits of this pattern in the previous post, so now we’ll reveal its implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="cm"&gt;/**
 * @summary The core of the application; registers and emits events
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sandbox&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;EventTarget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * @param {String[]} modules
   * @param {Function} callback
   */&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * An object with the `core` namespaces and event-related methods available to
     * all modules; provided in the callback to the `Sandbox` constructor
     */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;my&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="na"&gt;core&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;generateUUID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateUUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;getLoggerInstance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Bind event methods from the EventTarget (i.e., this) to ensure proper context&lt;/span&gt;
      &lt;span class="na"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Create factories for each module&lt;/span&gt;
    &lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;factories&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Lazily initialize the modules using `Object.defineProperty`&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factories&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// Here we create module lazily (i.e. when the module is accessed)&lt;/span&gt;
              &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s2"&gt;`INTERNAL_ERROR (sandbox): Could not create module (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;); ensure this module is registered via Sandbox.modules.of. See details -&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
              &lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Pass the sandbox object with `my` and `core` namespaces to the callback&lt;/span&gt;
    &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Though Sandbox extends EventTarget we *only* return a `dispatchEvent` method to
     * ensure that event registrations occur inside the Sandbox. This prevents
     * "eavesdropping" on events by clients that are not sandboxed. All such clients
     * can do is notify the sandbox of an external event of interest
     */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   *  Houses sandbox module definitions
   */&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * @param {String} moduleName - the identifier for the module to be referenced by
     * @param {Object} moduleClass - module's constructor
     */&lt;/span&gt;
    &lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moduleClass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;moduleClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation of our &lt;code&gt;Sandbox&lt;/code&gt; ensures that it &lt;em&gt;does not matter&lt;/em&gt; how dependencies are defined or ordered. Because they are lazily loaded, it means that dependent services are not created until they are used–even if a dependency is defined &lt;em&gt;after&lt;/em&gt; a service that relies upon it; it doesn't matter.&lt;/p&gt;

&lt;p&gt;Without lazy loading, services must be created in reverse topological order. This means first we create all the dependencies that have no dependencies themselves, then proceed to the ones that only depend on the already-created dependencies and so on until all services are created.&lt;/p&gt;

&lt;p&gt;The ability to define dependencies in any order is another attribute that keeps our architecture flexible.&lt;/p&gt;

&lt;p&gt;But how does it &lt;em&gt;work&lt;/em&gt;?  Let’s break it down.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Using Factory Functions for Modules&lt;/em&gt;: Instead of instantiating modules immediately in the &lt;code&gt;Sandbox&lt;/code&gt; constructor,  we create factory functions for each module and store them in a &lt;code&gt;factories&lt;/code&gt; object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Lazy Loading via getters&lt;/em&gt;: We use &lt;code&gt;Object.defineProperty&lt;/code&gt; to delay the creation of each module until it is accessed for the first time. This allows us to resolve dependencies dynamically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Passing the Sandbox&lt;/em&gt;: Above, we pass the reference to the &lt;code&gt;sandbox&lt;/code&gt; to each factory. Passing the &lt;code&gt;sandbox&lt;/code&gt; object to each module's constructor allows modules to reference each other through the &lt;code&gt;sandbox.my&lt;/code&gt; namespace.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
 &lt;span class="c1"&gt;// Lazily initialize the modules using `Object.defineProperty`&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factories&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// Here we create module lazily (i.e. when the module is accessed)&lt;/span&gt;
              &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s2"&gt;`INTERNAL_ERROR (sandbox): Could not create module (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;); ensure this module is registered via Sandbox.modules.of. See details -&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
              &lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In the example above our getter is actually &lt;em&gt;a function that creates&lt;/em&gt; the module.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lazy loading means deferring initialization of classes until they are actually &lt;strong&gt;needed&lt;/strong&gt;. Our getter function, shown above, checks to see if the module has been instantiated when the module is first accessed. If not, a factory function is called–this is the class that we register with the &lt;code&gt;Sandbox.modules.of&lt;/code&gt; method. The module is created then cached, ensuring each module is created only once.&lt;/p&gt;

&lt;p&gt;This strategy allows us to avoid creating all our modules up front, especially if some modules may never be used. It also saves time on initialization and memory.&lt;/p&gt;

&lt;p&gt;Dependency injection completes this architecture. All our services consume the sandbox as a constructor argument. This allows us to mock our sandbox API in unit tests and test our modules in complete isolation from the rest of our code. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At its simplest, &lt;strong&gt;Dependency Injection&lt;/strong&gt; means providing a method or class all of the things it needs in order to do its job. This can be achieved by passing dependencies as function or constructor arguments or by using a framework.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since our modules communicate with the rest of the application via the sandbox, we can place them anywhere in our code base without impacting peer modules. The Sandbox pattern, as shown above, gives us maximum flexibility when it comes to defining, executing, and organizing our code: ensuring that no matter what happens in the future, our application is ready to move at the speed of change.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Jump In: Sandboxes for Decoupled Applications</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Tue, 17 Sep 2024 13:50:06 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/jump-in-sandboxes-for-decoupled-applications-77j</link>
      <guid>https://dev.to/agustus_gloop/jump-in-sandboxes-for-decoupled-applications-77j</guid>
      <description>&lt;p&gt;A common theme of the last few posts has been containment: as a solution to invalid state changes, for managing exceptions and for unifying control flow.&lt;/p&gt;

&lt;p&gt;This approach has another name: encapsulation. It is a fundamental aspect of program design. &lt;em&gt;Why&lt;/em&gt;? Encapsulated programs are easier to manage; they keep things organized and they limit the scope of change required as new needs emerge.&lt;/p&gt;

&lt;p&gt;The oft-misquoted mantra of the &lt;em&gt;Single Responsibility Principle&lt;/em&gt; is worth repeating: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A class should have one and only one reason to &lt;strong&gt;&lt;em&gt;change&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of a codebase you know. How easy is it to change a class or a larger component without changing something else? If there is an update to the &lt;code&gt;PaymentService&lt;/code&gt;, does the &lt;code&gt;OrderService&lt;/code&gt; have to change too? &lt;/p&gt;

&lt;p&gt;Why are these second-order changes necessary and what is the order of difficulty in making them? The reply demonstrates how well or how poorly our systems absorb change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adapt Or Die
&lt;/h2&gt;

&lt;p&gt;In business, our ability to absorb change impacts our ability to &lt;em&gt;stay&lt;/em&gt; in business. &lt;/p&gt;

&lt;p&gt;How many times has your product manager insisted a change is not only urgent but required? How many times have you bristled at such a request owing to a keen awareness of the time such a change will take?&lt;/p&gt;

&lt;p&gt;“How long?” is invariably the next question from your product manager. They may ask about “level of effort”, which is another polite way of asking “How long?”&lt;/p&gt;

&lt;p&gt;Frustrating as it is, how long it takes to implement a change is the difference between maintaining competitiveness or making a needed improvement and watching as our business fumbles.&lt;/p&gt;

&lt;p&gt;What makes change difficult? Why do consequential changes take so long? A prime suspect is…dependencies.&lt;/p&gt;

&lt;p&gt;As the number of dependencies among modules grows, the greater the requirement of cascading modifications across our application and the greater the risk of instability across modules that directly depend on each other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhgbz2yt0wezhuv2tybw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhgbz2yt0wezhuv2tybw.png" alt="Image description" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It begins innocently enough...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugo6vpg647xvpv55301l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugo6vpg647xvpv55301l.png" alt="Image description" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...but as more modules are added, potential interactions and dependencies grow exponentially, not just linearly. Even a modest number of modules can be difficult to mentally model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When this network of modules grows too gnarly to reason about, the only recourse is to break our change into pieces over a span of weeks or months.&lt;/p&gt;

&lt;p&gt;This lead time gives competitors time to out-innovate us or for a bug in our program to persist longer than necessary; it also hampers the ability to experiment, which is the key to innovation.&lt;/p&gt;

&lt;p&gt;How can we limit the scope of change when updates are necessary? Further, how can we de-risk that change?&lt;/p&gt;

&lt;p&gt;Let's talk about the Sandbox pattern. For those that like details first, you can explore a detailed demo &lt;a href="https://stackblitz.com/edit/js-daoccb?file=event.js" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For a gentle introduction, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Come Out and Play
&lt;/h2&gt;

&lt;p&gt;This pattern is an oldie but a goodie. The exploration below is inspired by Nicholas Zakas's aging but excellent &lt;a href="https://www.youtube.com/watch?v=mKouqShWI4o&amp;amp;t=2611s" rel="noopener noreferrer"&gt;talk&lt;/a&gt; on creating an application framework.&lt;/p&gt;

&lt;p&gt;The implementation is less important, as Zakas makes clear. The benefits of the Sandbox pattern include enhanced modularity, reduced coupling and streamlined communication among application components. With this pattern, we set a foundation that ensures our applications and services can move at the speed of change.&lt;/p&gt;

&lt;p&gt;First we see create a new &lt;code&gt;Sandbox&lt;/code&gt;, which is an isolated context for our application modules to “play” in. The callback is where all the dependencies specified in the dependencies array are made available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// classes imported from wherever they are in the filesystem&lt;/span&gt;
&lt;span class="nx"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PaymentService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PaymentService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Where we use the services defined on the &lt;/span&gt;
   &lt;span class="c1"&gt;// sandbox aka where the action happens...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;box&lt;/code&gt; argument of our callback is an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget" rel="noopener noreferrer"&gt;&lt;code&gt;EventTarget&lt;/code&gt;&lt;/a&gt;, which means we can register an event listener via &lt;code&gt;addEventListener&lt;/code&gt;. Communication via events is key to decoupling our architecture; since our services have no knowledge of their peers they can only dispatch events to the sandbox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SystemEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./event.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * @param {Object} event
   * @param {IEvent&amp;lt;Object&amp;gt;} event.detail
   */&lt;/span&gt;
  &lt;span class="nf"&gt;onOrderReceived&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// do some order processing here...&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SystemEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ORDER_FULFILLED&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;When the Sandbox is initialized it provides access to registered services. Above we send an &lt;code&gt;ORDER_FULFILLED&lt;/code&gt; event to the Sandbox after an order is processed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Only the sandbox has knowledge of registered services. When an event of interest is dispatched, the sandbox calls an appropriate handler, which receives data about the event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PaymentService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ORDER_RECEIVED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onOrderReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createInvoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="c1"&gt;// assuming our OrderService dispatches event&lt;/span&gt;
      &lt;span class="c1"&gt;// when the order is fulfilled  &lt;/span&gt;
      &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ORDER_FULFILLED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onOrderFulfilled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;/blockquote&gt;

&lt;p&gt;This architecture allows us to write isolated classes with no dependencies and clearly defined responsibilities. We have freedom to move services anywhere at any time in our app with no changing costs. For best results we limit ourselves to importing only types, interfaces, NPM packages and files located within the same folder; this gives us maximum maneuverability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking Boundaries with Cross-Sandbox Events
&lt;/h2&gt;

&lt;p&gt;The Sandbox pattern also offers another organizational unit of our code. For example, if we want to create multiple sandboxes to group services into specific domains. Since the &lt;code&gt;Sandbox&lt;/code&gt; constructor returns an object with a &lt;code&gt;dispatchEvent&lt;/code&gt; method, we can publish events &lt;em&gt;across&lt;/em&gt; sandboxes without exposing the sandboxed services themselves: this means maximum modularity, maximum range of motion and a clear separation of concern for each of our services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// here we have a sandbox instance solely focused on &lt;/span&gt;
&lt;span class="c1"&gt;// shipping-related concerns...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yourSandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHIPMENT_READY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// do stuff related to preparing to ship the order...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PaymentService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ORDER_RECEIVED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onOrderReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createInvoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ORDER_FULFILLED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onOrderFulfilled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// communication across sandboxes is facilitated through events too...&lt;/span&gt;
        &lt;span class="nx"&gt;yourSandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SystemEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHIPMENT_READY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Our dispatched events are instances of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent" rel="noopener noreferrer"&gt;&lt;code&gt;CustomEvent&lt;/code&gt;&lt;/a&gt; interface&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are familiar with the classic design patterns, you will notice this implementation is a combination of the &lt;a href="https://www.dofactory.com/javascript/design-patterns/mediator" rel="noopener noreferrer"&gt;Mediator&lt;/a&gt; and &lt;a href="https://dev.to/anishkumar/design-patterns-in-javascript-publish-subscribe-or-pubsub-20gf"&gt;Publish/Subscribe&lt;/a&gt; patterns.&lt;/p&gt;

&lt;p&gt;As always, the exact implementation is less important than the inspiration. Whether you roll your own variety of this type of architecture or use an established framework, the principles of event-driven design and &lt;a href="https://blog.codeminer42.com/dependency-injection-in-js-ts-part-1/#cyclic-dependencies" rel="noopener noreferrer"&gt;dependency injection&lt;/a&gt; set a solid foundation for any large application. &lt;/p&gt;

&lt;p&gt;Through their judicious use, these principles will serve your programs well no matter what they aim to achieve–enabling them to move at the speed of change.&lt;/p&gt;

&lt;p&gt;Stay tuned for an even deeper dive into this pattern! In the next post we get into the weeds of just &lt;em&gt;how&lt;/em&gt; dependency injection and lazy loading enhance the flexibility of our Sandboxes.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Keeping 'em Honest: Self-Validating Objects</title>
      <dc:creator>Sean Travis Taylor</dc:creator>
      <pubDate>Tue, 10 Sep 2024 14:05:56 +0000</pubDate>
      <link>https://dev.to/agustus_gloop/keeping-em-honest-self-validating-objects-20bf</link>
      <guid>https://dev.to/agustus_gloop/keeping-em-honest-self-validating-objects-20bf</guid>
      <description>&lt;p&gt;What is a program? A program is a series of state changes that accomplishes something. It may be a game or a piece of CAD software. It may be a graphic design application or a script that automates data entry. Whatever the endeavor, a program's purpose is to do something and achieve that something over a series of changes in state. &lt;/p&gt;

&lt;p&gt;A simple calculator offers an example. We begin with a value and through a series of operations (state changes) we produce a result: a sum, a product, a difference or a dividend. &lt;/p&gt;

&lt;p&gt;What is a bug? A programmer's headache certainly but what is it really? It is a result of an invalid state change: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;division by zero. A compiler will reject this computation &lt;/li&gt;
&lt;li&gt;sending an HTTP &lt;code&gt;POST&lt;/code&gt; request to create a new resource without authorization&lt;/li&gt;
&lt;li&gt;subtracting a number from an array via the minus (&lt;code&gt;-&lt;/code&gt;) operator &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list above presents invalid state changes within a programming language or within the business rules of an application. The programmer's mandate is to avoid such changes; when they occur, it is the programmer's job to ensure the program recovers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standing Guard
&lt;/h2&gt;

&lt;p&gt;Strong typing helps in this regard. So do guard clauses and validation packages like Joi or Ajv. If none of these are in place in our programs, we should begin with the above. Such approaches are certainly better than nothing yet fail to aid the program at its most vulnerable: during execution. &lt;/p&gt;

&lt;p&gt;Guard clauses require sprinkling if/else statements throughout our codebases and they create new states our program can assume. They also require immense rigor. We discussed the dangers of a gauntlet of if/else statements in a previous &lt;a href="https://dev.to/agustus_gloop/seal-the-exits-rethinking-the-ifelse-statement-383l"&gt;post&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Type-safe languages or typing packages offer confidence but serve best as we &lt;em&gt;write&lt;/em&gt; code. They prevent introduction of invalid state changes into the program in the first place. Yet whether provided by a programming language or some third-party package, strong typing offers less protection when our program runs.&lt;/p&gt;

&lt;p&gt;At runtime we pray we've done our best to catch exceptions whenever they occur and at least log a descriptive message that leaves us some clue. The current state of the art encourages us to catch invalid state changes through vigilance and explicit checks. We can also just wait for the program to explode. &lt;/p&gt;

&lt;h2&gt;
  
  
  The $64,000 Question
&lt;/h2&gt;

&lt;p&gt;How can we design objects such that invalid state transitions instantly create exceptions? &lt;/p&gt;

&lt;p&gt;How do we avoid passing invalid data around our application until a guard clause or validation exercise uncovers an invariant is violated? &lt;/p&gt;

&lt;p&gt;Can we make invalid state transitions impossible? &lt;/p&gt;

&lt;p&gt;We present the Self-validating Object: an object that ensures its own validity and notifies us when validation criteria are violated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Ajv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ajv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @param {Object} schema - a JSON Schema document to use for validation
 * @param {Object} options - valid JSON Schema configuration option see: https://ajv.js.org/options.html
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ajv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Ajv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Runs validation of an incoming object
   * @param {Object} data - an object to be validated against the `schema`
   */&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ajv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Validation error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ajv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Factory function for Self-Validating Objects
 * @param {Object} schema - a JSON Schema object
 * @param {Object} initialData
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SelfValidatingObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;__data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;initialData&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * @throws Will throw exception if data validation fails
   */&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;__data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Set the new value&lt;/span&gt;
        &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Validate new data state; may throw exception&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Indicate successful property set&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;__data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Provides access to the whole object&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;__data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// Default get operation&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Initial validation of the provided data&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our implementation uses &lt;a href="https://jsonschema.net/" rel="noopener noreferrer"&gt;JSON Schema&lt;/a&gt; and &lt;a href="https://ajv.js.org/" rel="noopener noreferrer"&gt;Ajv&lt;/a&gt; as a validation handler but you can adjust this pattern with your own validation library as well.&lt;/p&gt;

&lt;p&gt;The key to this pattern is the ES6 &lt;code&gt;Proxy&lt;/code&gt; class. We won't explore this class in this post but you can learn more &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://ponyfoo.com/articles/es6-proxies-in-depth" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Proxy&lt;/code&gt; class intercepts changes on an object. Our implementation automatically validates the object whenever a field is set or updated. This validation is executed against the JSON Schema we provide on object creation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fooObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SelfValidatingObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;12345&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Object is valid:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fooObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// This throws an exception because '123' is not a valid string for 'bar'&lt;/span&gt;
  &lt;span class="nx"&gt;fooObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1234&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Staying in Touch
&lt;/h2&gt;

&lt;p&gt;We learn the instant an object enters an invalid state and raise an exception. No stepping through code to learn when an object property becomes &lt;code&gt;undefined&lt;/code&gt;. Our exception also includes the breached validation rules for our logs. This means no more logging objects to trace surprising state changes.&lt;/p&gt;

&lt;p&gt;Since validation logic is encapsulated within our object, we know the validity of our object wherever it travels in our codebase. &lt;/p&gt;

&lt;p&gt;When Self-Validating Objects are combined with patterns explored in previous posts, we get a robust codebase with fewer bugs owing to unpredictable state changes. &lt;/p&gt;

&lt;p&gt;Self-Validating Objects render it impossible for objects to exist in invalid states. Whether it's the implementation shown above or something different, thinking deeply about how to prevent invalid state changes is a key activity during the design of any program.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
