<?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: TC Ricks</title>
    <description>The latest articles on DEV Community by TC Ricks (@tc87).</description>
    <link>https://dev.to/tc87</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%2F433712%2F90a275bd-321d-4a60-a6a6-ac42fd54f178.jpeg</url>
      <title>DEV Community: TC Ricks</title>
      <link>https://dev.to/tc87</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tc87"/>
    <language>en</language>
    <item>
      <title>Why AI agents fail without a Data Layer</title>
      <dc:creator>TC Ricks</dc:creator>
      <pubDate>Wed, 29 Oct 2025 18:01:07 +0000</pubDate>
      <link>https://dev.to/tc87/why-ai-agents-fail-without-a-data-layer-2pob</link>
      <guid>https://dev.to/tc87/why-ai-agents-fail-without-a-data-layer-2pob</guid>
      <description>&lt;p&gt;AI agents can reason. They can plan, summarize, and even write SQL.&lt;br&gt;&lt;br&gt;
But they can’t fix messy data.  &lt;/p&gt;

&lt;p&gt;That gap between reasoning and reality is where most AI projects stall, not because the models don’t work, but because the data underneath them is too fragmented to think with.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hidden bottleneck
&lt;/h2&gt;

&lt;p&gt;Every company wants to give their business users an AI assistant that can answer questions like:&lt;/p&gt;

&lt;p&gt;"Which campaigns drove the most revenue this quarter?"  &lt;/p&gt;

&lt;p&gt;Or even more ambitious:  &lt;/p&gt;

&lt;p&gt;"If we increase ad spend by 10% across platforms, what happens to conversion cost?"&lt;/p&gt;

&lt;p&gt;The problem isn’t the prompt.&lt;br&gt;&lt;br&gt;
It’s that the model has to reason over conflicting definitions of “campaign,” multiple “revenue” columns, and data scattered across CRMs, ad tools, and spreadsheets.  &lt;/p&gt;

&lt;p&gt;The result is confident answers that are wrong.&lt;/p&gt;

&lt;p&gt;Large language models can generate logic, but they can’t repair the structure that logic depends on.&lt;br&gt;&lt;br&gt;
They don’t know which system is right when numbers disagree, and even if you’re able to give them guidance, it’s forgotten the moment you start a fresh conversation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reasoning requires ground truth
&lt;/h2&gt;

&lt;p&gt;Think of an LLM as a sharp analyst dropped into a disorganized office.  &lt;/p&gt;

&lt;p&gt;They can ask smart questions, make connections, and even propose insights, but if every folder has its own version of “customer_data_final_v2,” they’ll still get lost.  &lt;/p&gt;

&lt;p&gt;LLMs need a consistent, unified layer underneath them to reason safely.  &lt;/p&gt;

&lt;p&gt;That layer provides:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entity resolution (so “user,” “lead,” and “customer” point to the same person)
&lt;/li&gt;
&lt;li&gt;Normalization (so “spend” always means the same thing)
&lt;/li&gt;
&lt;li&gt;Proof and lineage (so you can see why the AI answered the way it did)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without it, the reasoning is just pattern-matching on chaos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example: Why AI struggles with marketing data
&lt;/h2&gt;

&lt;p&gt;Consider a simple question:&lt;br&gt;&lt;br&gt;
"Which campaigns had the highest ROI this month?"&lt;/p&gt;

&lt;p&gt;The data for that question usually lives across multiple tools:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LinkedIn Ads: campaign performance data such as impressions, clicks, conversions, and spend
&lt;/li&gt;
&lt;li&gt;Reddit Ads: campaign performance data that captures similar metrics but uses different naming conventions, attribution logic, and data structures
&lt;/li&gt;
&lt;li&gt;Google Sheets: manually tracked pipeline and revenue data that connects campaign activity to actual business outcomes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first glance, this looks easy to join. Both ad platforms are reporting the same kind of metrics.&lt;br&gt;&lt;br&gt;
In practice, the schema, field names, and even metric logic differ enough that an LLM can’t cleanly reconcile them.&lt;/p&gt;

&lt;p&gt;For example:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reddit might call a column “Total Spent (USD)” while LinkedIn calls it “Amount Spent.”
&lt;/li&gt;
&lt;li&gt;Reddit’s conversions might be attributed by “View” and “Click,” while LinkedIn uses “Post Click” and “Post View.”
&lt;/li&gt;
&lt;li&gt;The underlying campaign IDs differ across both systems, and neither directly ties back to the deal IDs tracked in Sheets.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the problem isn’t that the data types are different.&lt;br&gt;&lt;br&gt;
It’s that the definitions, identifiers, and relationships are inconsistent across tools, and no AI model can reliably resolve that structure automatically.&lt;/p&gt;




&lt;h3&gt;
  
  
  What that inconsistency looks like in practice
&lt;/h3&gt;

&lt;p&gt;Problem Example:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mismatched identifiers: LinkedIn uses “Campaign ID,” Reddit uses “Campaign Name,” and the Sheet uses free-text entries like “Spring Promo.” The AI can’t reliably join them.
&lt;/li&gt;
&lt;li&gt;Inconsistent schemas: LinkedIn and Reddit measure engagement differently. One uses CTR, the other uses CPE. The model treats them as comparable.
&lt;/li&gt;
&lt;li&gt;Misaligned time frames: Reddit reports in UTC, LinkedIn in local time, and the Sheet updates once a week. The AI merges mismatched periods.
&lt;/li&gt;
&lt;li&gt;Different metric logic: The Sheet’s “revenue” column includes both “won” and “lost” deals, while ad platforms count all spend as active.
&lt;/li&gt;
&lt;li&gt;No persistent schema: Even if the AI fixes these issues once, it doesn’t remember them for the next query.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output looks intelligent. It returns numbers and charts, but the logic underneath is incoherent.  &lt;/p&gt;

&lt;p&gt;What looks like a small naming issue becomes a silent failure mode when these systems feed an LLM.  &lt;/p&gt;

&lt;p&gt;The model isn’t lying; it’s improvising with incomplete context.&lt;/p&gt;




&lt;p&gt;To answer that question correctly, several structural steps need to happen first:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Entity resolution: connect “Spring Promo” across Sheets, LinkedIn, and Reddit even when IDs or names differ.
&lt;/li&gt;
&lt;li&gt;Normalization: unify currencies, time zones, and date formats.
&lt;/li&gt;
&lt;li&gt;Semantic alignment: define what “revenue” and “ROI” actually mean across all systems.
&lt;/li&gt;
&lt;li&gt;Proof and lineage: show exactly how those numbers were derived.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this can be done reliably by an LLM alone.&lt;/p&gt;

&lt;p&gt;It requires a persistent data layer that understands relationships, definitions, and meaning.&lt;br&gt;&lt;br&gt;
Once that layer exists, the AI can reason instead of guess.  &lt;/p&gt;

&lt;p&gt;The same prompt, “Which campaigns had the highest ROI this month?”, now produces consistent, explainable results across every source.&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%2Ft27gptjr8x92df2bch0i.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%2Ft27gptjr8x92df2bch0i.png" alt="How AstroBee works" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In AstroBee, you can literally see every system mapped to the same entity definitions so “customer,” “lead,” and “user” finally mean the same thing.&lt;/p&gt;




&lt;h3&gt;
  
  
  And this story repeats everywhere
&lt;/h3&gt;

&lt;p&gt;Marketing is only one example.&lt;br&gt;&lt;br&gt;
The same pattern plays out across every function in a business:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product managers want to connect feature usage data with revenue outcomes, but product analytics and billing rarely share entity definitions.
&lt;/li&gt;
&lt;li&gt;Sales teams track leads in CRMs that don’t match marketing definitions of “qualified,” making pipeline reports unreliable.
&lt;/li&gt;
&lt;li&gt;Operations and finance rely on metrics from systems that calculate costs differently across regions or business units.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each role faces the same challenge: they know what questions to ask, but not how to unify the context to get trustworthy answers.  &lt;/p&gt;

&lt;p&gt;Without a data layer to standardize meaning, every query becomes a one-off cleanup job instead of a reusable insight.&lt;/p&gt;




&lt;h2&gt;
  
  
  Business users can’t build this infrastructure themselves
&lt;/h2&gt;

&lt;p&gt;Before AstroBee, the only people who could create this kind of foundation were data engineers and analytics teams.&lt;br&gt;&lt;br&gt;
That meant every marketing manager, product lead, or ops analyst had to file tickets and wait weeks just to get clean data to work with.  &lt;/p&gt;

&lt;p&gt;It’s the same bottleneck data scientists faced years ago.&lt;br&gt;&lt;br&gt;
They could train models, but couldn’t deploy them without infra engineers.  &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%2Fu558iyqlxbgddjltqzw2.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%2Fu558iyqlxbgddjltqzw2.png" alt="AstroBee vs Modern Data Stack" width="800" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now business users face the same wall.&lt;br&gt;&lt;br&gt;
They understand the business context better than anyone, but don’t have the tools to access or unify their data effectively.  &lt;/p&gt;

&lt;p&gt;They don’t need another dashboard or prompt template.&lt;br&gt;&lt;br&gt;
They need a system that builds the data foundation for them, so AI can actually reason with the business context they know best.&lt;/p&gt;




&lt;h2&gt;
  
  
  The foundation matters more than the model
&lt;/h2&gt;

&lt;p&gt;A great model sitting on messy data is like a Ferrari on gravel.&lt;br&gt;&lt;br&gt;
No matter how powerful it is, it’s not going anywhere fast.  &lt;/p&gt;

&lt;p&gt;No matter how powerful the LLM is, if the data foundation is incomplete, automated reasoning will fail.  &lt;/p&gt;

&lt;p&gt;The real innovation isn’t another smarter LLM.&lt;br&gt;&lt;br&gt;
It’s the layer that makes your company’s data understandable, traceable, and ready for reasoning.  &lt;/p&gt;

&lt;p&gt;That’s what AstroBee focuses on, creating a unified data layer that lets business users query, explore, and automate decisions confidently.  &lt;/p&gt;

&lt;p&gt;Because the future of AI in business isn’t about bigger models.&lt;br&gt;&lt;br&gt;
It’s about giving every user solid ground to stand on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;AI doesn’t fail because it’s not smart enough.&lt;br&gt;&lt;br&gt;
It fails because it doesn’t understand your business.  &lt;/p&gt;

&lt;p&gt;The fix isn’t a better chatbot.&lt;br&gt;&lt;br&gt;
It’s a better foundation.  &lt;/p&gt;

&lt;p&gt;If this challenge sounds familiar, we’re always open to comparing notes.&lt;br&gt;&lt;br&gt;
Feel free to reach out at &lt;a href="mailto:tc@astrobee.ai"&gt;tc@astrobee.ai&lt;/a&gt; to talk through what a cleaner data foundation could look like.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devrel</category>
      <category>analytics</category>
      <category>dataintegration</category>
    </item>
    <item>
      <title>Where MCP falls short: data integration in the AI world</title>
      <dc:creator>TC Ricks</dc:creator>
      <pubDate>Mon, 11 Aug 2025 18:21:39 +0000</pubDate>
      <link>https://dev.to/tc87/where-mcp-falls-short-data-integration-in-the-ai-world-9aa</link>
      <guid>https://dev.to/tc87/where-mcp-falls-short-data-integration-in-the-ai-world-9aa</guid>
      <description>&lt;p&gt;Connecting to disparate source systems is a solved problem. Tools like FiveTran, AirByte, and merge.dev sink thousands of hours of engineering work into supporting connectors to every software system on the planet, and most teams who supporting 1,000s of connectors can use one of these tools under the hood to get there.&lt;/p&gt;

&lt;p&gt;Once data is connected, you still have the problem of data representation. Each of your source systems (like HubSpot and Intercom, or your billing system and product database), will represent data in their own way. That’s what you get when you connect to APIs, you must speak the language of the API provider. It’s what you get when you connect to MCP servers, too. It’s the same structure, and presents data the same way.&lt;/p&gt;

&lt;p&gt;If you connect an LLM to 5 different MCP servers and then ask for information regarding your customer, say Acme Incorporated , your answer will be degraded by the fact that each source system might represent customers differently. Maybe in HubSpot, companies are represented by their primary domain, getacme.com . Maybe in your billing system, they’re represented by the “Doing-business-as” name; Acme. And then maybe in your product, there are three subsidiaries that have engaged and are submitted support requests in Intercom under the names of those subsidiaries.&lt;/p&gt;

&lt;p&gt;For those who are dead-set on AI enablement, there are two options available to solving this problem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resolve Entities on Inference: Trust the LLM to expend enough tokens in “thought” to scan through all of the data available in each source system via its MCP connections to exhaustively search around the Acme Incorporated concept to figure out where data related to Acme lives in your systems, and then report on it&lt;/li&gt;
&lt;li&gt;Resolve Entities Ahead of Time: Use traditional data processing techniques to reveal gaps in entity resolution prior to inference, and then use inputs from business stakeholders and data experts to resolve entities in an integrated “source of truth” layer. LLMs refer to this layer on inference, instead of raw source systems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you just upload small amounts of data to Chat-GPT or Claude, it seems like the first option will work great. LLMs are very good at this semantic search and conceptual construction work. Your CSVs fit entirely into the context window of the LLM, and every token emitted is constructively contributing to grouping like elements together.&lt;/p&gt;

&lt;p&gt;The moment you start working on larger datasets, the first option falls apart. For large scale search-and-compute, LLM-driven crawling is painfully slow compared to traditional data processing techniques. Semantic search works great at larger scales, for what it does, but is different altogether from reasoning occurring within one context window.&lt;/p&gt;

&lt;p&gt;You could view this as an issue of context window limitations, but I think this is short-sighted. The context window is what it is. In the medium term, at least, it seems like it’ll always be a factor we need to design around, in the same way that RAM is still a factor to design around no matter how much memory we manage to get on our laptops. I think the context window is just the wrong tool for the job. Use token-based compute for what its good at, and use classical compute for what it’s good at: large scale data processing.&lt;/p&gt;

&lt;p&gt;At AstroBee, we don’t foresee a world where MCP alone is the primary way to get strong AI capabilities across your enterprise. We see MCP working great for specific tool calling, and faltering when data needs to be reconciled across tools in order to make good decisions.&lt;/p&gt;

&lt;p&gt;AstroBee integrates your data for you, so that entity resolution across your different systems happens before inference, and with the right tools: a mixture of SQL-driven integration, semantic search, and a touch of LLM-driven reasoning. Then, when AI agents work over your enterprise data, they work with pre-integrated data, delivering significantly better results and making significantly better decisions than before.&lt;/p&gt;

&lt;p&gt;To see an alpha research preview of AstroBee, visit us at &lt;a href="https://astrobee.ai" rel="noopener noreferrer"&gt;https://astrobee.ai&lt;/a&gt;. If you’d like to discuss our approach and give feedback or special requests, contact me directly at &lt;a href="mailto:support@astrobee.ai"&gt;support@astrobee.ai&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>llm</category>
      <category>datascience</category>
      <category>ai</category>
    </item>
    <item>
      <title>What the heck is an Ontology?</title>
      <dc:creator>TC Ricks</dc:creator>
      <pubDate>Fri, 08 Aug 2025 15:50:39 +0000</pubDate>
      <link>https://dev.to/tc87/what-the-heck-is-an-ontology-40kc</link>
      <guid>https://dev.to/tc87/what-the-heck-is-an-ontology-40kc</guid>
      <description>&lt;p&gt;Is Palantir onto something, or are they just blowing hot air?&lt;/p&gt;

&lt;p&gt;You may have heard the term Ontology, and why leading businesses rely on them to build trustworthy data models. Recent advances in LLMs have made it possible for startups and enterprises alike to build flexible, reliable semantic layers faster than ever before.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do we mean by “ontology”?
&lt;/h2&gt;

&lt;p&gt;The ontology has its roots in philosophy, where it represents a formal catalog of objects, the properties that describe them, and the relationships that connect them. Here’s a simple example:&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%2Flhswdiyi9r5uqssd0hru.webp" 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%2Flhswdiyi9r5uqssd0hru.webp" alt="Example Simple Coffee Ontology" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might notice that this begins to resemble a data model. The Ontology has become popularized by Palantir as a semantic layer solution, which, to be reductive, is essentially a relational database with a semantic structure (different from the web 2.0 semantic ontology). This Ontology acts as an abstraction between raw data and the business concepts they represent. There are many implementations of semantic layers under a variety of names, but the bottom line is they’re working to make data represent our real world as we understand it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do people want semantic layers anyway?
&lt;/h2&gt;

&lt;p&gt;Every term you’ve heard — source of truth, golden tables, semantic layer — is chasing the same promise: let anyone ask and receive answers that faithfully reflect how the business works. An analyst should be able to ask, “How many premium users bought espresso last month?” and trust the answer. Well-implemented Ontologies deliver on that promise by encoding business logic and enforcing queries adhere to that model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ontologies in the Real World
&lt;/h2&gt;

&lt;p&gt;As you might guess, this isn’t a new idea.&lt;/p&gt;

&lt;p&gt;The star schema, introduced in the 1990s, puts a wide fact table at its center and fan‑outs to dimension tables on the edges. It celebrates speed and simplicity; query planners can predict access patterns, and BI tools understand the layout instinctively. This modeling approach forces data into either “dimensions” or “facts”. Facts generally have many, many rows with a few columns. The idea is to represent that which is large and highly repeatable row-over-row.&lt;/p&gt;

&lt;p&gt;However, this hard divide between facts (immutable events) and dimensions (reference data) falls apart as the business evolves-say, a new pricing plan blurs the line between a transaction and a subscription, those early modeling choices become concrete shoes.&lt;/p&gt;

&lt;p&gt;Snowflake schemas attempt to tame redundancy by further normalizing dimension tables. Storage usage drops, but the price is paid in join complexity. What once took a single hop now requires a small expedition through intermediary tables, and each extra join is another surface for error or performance regressions.&lt;/p&gt;

&lt;p&gt;Both designs inherit the fact‑dimension mindset. It has served analytics well for decades, but it assumes the world stays still long enough for engineers to carve data into neat shapes. Modern businesses rarely stay that sessile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Palantir’s Ontology: turning the model into a graph
&lt;/h2&gt;

&lt;p&gt;Palantir threw out the fact‑dimension wall and treated every table as a node in a directed graph. Links between nodes come with explicit types, one‑to‑one, one‑to‑many, and so forth, so analysts can traverse the graph without guessing how joins should behave. The model is also iterative. If tomorrow’s reality demands a new object or a new relationship, you add a node or an edge and keep moving. Query optimizers still have plenty to chew on, but modelers are no longer forced to contort reality to fit a star pattern decided months earlier when the business looked dramatically different.&lt;/p&gt;

&lt;p&gt;Palantir’s Ontology reflects current semantic logic while remaining fluid enough to reflect the changing reality of the business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why volatility hurts everyone-enterprises and startups alike
&lt;/h2&gt;

&lt;p&gt;Unfortunately, it is incredibly challenging to build a solid, trustworthy source of truth that is also flexible enough to reflect a fast-moving environment.&lt;/p&gt;

&lt;p&gt;Consider an enterprise that suddenly faces a new regulatory regime or an unexpected cybersecurity threat. Core schemas that once changed quarterly may now shift weekly as compliance teams add tables, columns, or entire data sources. Meanwhile, a fast‑growing startup is rewriting its own playbook-pivoting products, experimenting with pricing, or integrating a surge of user‑generated events. In both cases, pipelines that were handcrafted for yesterday break under the weight of today’s questions. The cost is measured in stale dashboards, duplicate metrics, and lost decisions.&lt;/p&gt;

&lt;p&gt;Palantir gets around this problem by using the forward-deployed engineer model; embedded analysts who work to deeply understand how a business operates across industries, then task themselves with manually, and often painstakingly, maintaining an Ontology for a Fortune 500 company. Meanwhile, startups simply accept analytical debt as the price of speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  LLMs have changed the game: automatic ontology generation
&lt;/h2&gt;

&lt;p&gt;Large‑language models have changed the economics of generating and maintaining Ontologies. Point them at your data warehouses and they can take a massive snapshot of the current state of your data. They can then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scan thousands of objects, notice common naming conventions, and summarize as a data model&lt;/li&gt;
&lt;li&gt;Spot tables that share primary keys or semantic similarities in values, and propose relationships&lt;/li&gt;
&lt;li&gt;Propose relationships between objects with cardinality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What previously required weeks of an expensive embedded analyst working to understand every nook and cranny of your data warehouse, an LLM can gain a version of that understanding in moments. The challenge is that LLMs, unlike a human analyst, don’t innately know that your “customer” excludes free‑trial users or the specific way your company calculates key metrics. Without guidance, they hallucinate, generalize, or miss edge cases. That’s where human context and Astrobee come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Astrobee, the collaboration layer
&lt;/h2&gt;

&lt;p&gt;Astrobee is the collaboration layer between subject‑matter experts and the LLM runtime.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract -We ingest your warehouse and scrape lineage to give the LLM its raw material.&lt;/li&gt;
&lt;li&gt;Draft — The model proposes objects, links, tests, plus the SQL and pipeline code to populate them.&lt;/li&gt;
&lt;li&gt;Review — Stakeholders approve, comment, or override in a Git‑style diff. Astrobee records every decision.&lt;/li&gt;
&lt;li&gt;Democratize Data — Anyone across your business can query your data, and rest assured they’re all referencing a single source of truth, Ontology&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As more questions flow through Astrobee, it spots patterns and optimizes itself. Repeated joins become reusable generated pipelines. Expensive ad‑hoc queries trigger recommendations to use specific measures org wide.&lt;/p&gt;

&lt;p&gt;The result is compound leverage: each inquiry refines the ontology, and consequently, business insights, for subsequent users. Enterprises manage schema drift without large data teams, and startups achieve enterprise-grade modeling agility at seed-stage budgets.&lt;/p&gt;

&lt;p&gt;If this sounds interesting to you, we’d love to chat. Drop us a line at &lt;a href="mailto:hi@astrobee.ai"&gt;hi@astrobee.ai&lt;/a&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>ai</category>
      <category>llm</category>
      <category>ontology</category>
    </item>
    <item>
      <title>Kimball Star Schema vs Palantir’s Ontology</title>
      <dc:creator>TC Ricks</dc:creator>
      <pubDate>Wed, 06 Aug 2025 15:38:34 +0000</pubDate>
      <link>https://dev.to/tc87/kimball-star-schema-vs-palantirs-ontology-41pe</link>
      <guid>https://dev.to/tc87/kimball-star-schema-vs-palantirs-ontology-41pe</guid>
      <description>&lt;p&gt;Here’s a joke I thought of:&lt;/p&gt;

&lt;p&gt;Q: What does the father of data warehousing have in common with a $300B analytics powerhouse named after a Lord of the Rings spying device?&lt;/p&gt;

&lt;p&gt;A: Both of them defy the star schema paradigm and encourage you to model your data top-down, in a highly normalized format.&lt;/p&gt;

&lt;p&gt;…maybe you didn’t think that was funny, but I did. Not funny in a “ha-ha” way, but in a glitch-in-the-matrix kind of way. Like “huh, that’s funny”.&lt;/p&gt;

&lt;p&gt;Pretty much everyone in data today uses some variation of the star schema paradigm popularized by Ralph Kimball. If you’re familiar with data warehousing or business intelligence, you’ve probably seen “facts” and “dimensions” tables floating around in your tools. Those are Kimball’s work and they really are the industry standard for modeling data for BI purposes. They’re everywhere.&lt;/p&gt;

&lt;p&gt;Palantir, a $300B data analytics company, makes a ton of money selling services that model enterprise data in a way that runs directly counter to the Kimball industry standard. They center their offering around the ontology, a highly normalized data model that represents the “objects” a business user would think about. These objects have links between them characterized as 1-to-1, 1-to-many, or many-to-many. It’s a highly intuitive structure.&lt;/p&gt;

&lt;p&gt;The weird thing is that most of the industry doesn’t do that. Palantir’s roaring success in deploying teams of forward deployed engineers to build out ontologies in their platform is a testament to the power of this modeling technique. But usually there’s nothing new under the sun. So why isn’t anyone else doing this?&lt;/p&gt;

&lt;p&gt;To me, as a former Palantir forward deployed engineer, it felt like a glitch in the matrix. It didn’t quite make sense.&lt;/p&gt;

&lt;p&gt;I would come across customer data warehouses which were highly denormalized and structured in “facts” and “dimensions”. I asked myself why on earth they adopted this strategy, instead of the more intuitive, straightforward object-property approach. Following this train of thought, I uncovered a fascinating story of a passionate but respectful technological debate between titans of the data industry, Bill Inmon and Ralph Kimball.&lt;/p&gt;

&lt;p&gt;Bill Inmon, from the beginning, advocated for a top-down approach to data modeling. He felt enterprises should establish what they need from their data warehouse in a single source of truth, and then do the integration work upfront to map raw data from source systems into the source of truth in a highly consistent way. He advocated for a normalized layer (many tables, not pre-joined), pushing joins until the very end of the analytics process. This maximizes data availability, ensuring information is not aggregated away or dropped in “join” steps before it reaches the end user.&lt;/p&gt;

&lt;p&gt;Ralph Kimball, on the other hand, is largely responsible for the “dimensional modeling” approach that most people use today. You may know this as the “star schema”. The approach has a lot of great features that I love: it’s incremental, decentralized, simpler up-front, and returns user-facing results quickly. It’s a “bottoms up” methodology; you can incrementally deliver analytics from each source system in your organization before you create a centralized, consistent source of truth. As a result of these really great properties, Kimball’s approach has achieved full ideological capture of the data warehousing community.&lt;/p&gt;

&lt;p&gt;So what’s going on with Palantir? Why did they go so off-track of the consensus?&lt;/p&gt;

&lt;p&gt;I don’t think they’re contrarian for the sake of being contrarian. Palantir sells contracts on business results and the price tag is high. High enough to be able to deploy specialized, highly skilled resources with the sole goal of developing a data model that achieves 7 or 8 figure business outcomes. In this environment, Palantir is able to pay the heavy up-front human cost of the Inmon, top-down approach: integrate all the data you can, pay down the effort to achieve strong consistency across disparate data sources, and deliver a high-level model that fully delivers on data availability in a human-understandable way (the ontology).&lt;/p&gt;

&lt;p&gt;Most data teams don’t have the luxury of the budget, focus, and firepower of Palantir to build and maintain an ontology. Kimball’s approach is far easier, but comes with the downside of a lack of data visibility and coverage for the business user. Usually people close the gap with fancy dashboards and on-call schedules for analysts who respond to ad-hoc requests from business users.&lt;/p&gt;

&lt;p&gt;AI is rapidly changing what people can do with a limited amount of budget and human firepower. In my opinion, now is the time to invest in technology that cuts the expense of ontology curation by 90%. That’s what we’re working on at AstroBee. We’re using LLM-based agents to do the upfront integration and data consistency work required to bring a full-coverage ontology to your team at an affordable price point. You could say that AstroBee brings the firepower of Palantir to the little guy. We’re gearing up to launch soon, but if you’d like to take part in early pre-launch user testing, please reach out to &lt;a href="mailto:support@astrobee.ai"&gt;support@astrobee.ai&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>datascience</category>
      <category>starschema</category>
      <category>ontology</category>
    </item>
    <item>
      <title>Develop on a branch of your remote cluster with Tilt and Kardinal</title>
      <dc:creator>TC Ricks</dc:creator>
      <pubDate>Fri, 11 Oct 2024 17:45:07 +0000</pubDate>
      <link>https://dev.to/tc87/develop-on-a-branch-of-your-remote-cluster-with-tilt-and-kardinal-59mh</link>
      <guid>https://dev.to/tc87/develop-on-a-branch-of-your-remote-cluster-with-tilt-and-kardinal-59mh</guid>
      <description>&lt;p&gt;Tilt is a powerful tool for microservice development, automating many steps including watching files, building images, and keeping an up to date environment. However, its effectiveness is often limited to local Kubernetes clusters. For stacks that require remote development clusters, the operational overhead with using Tilt can become a significant hurdle.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;&lt;em&gt;Kardinal&lt;/em&gt;&lt;/strong&gt;, an open-source solution for spinning up extremely lightweight ephemeral development environments within a shared Kubernetes cluster. By leveraging request-level routing, Kardinal maximizes the reuse of microservices and databases while maintaining isolation. An environment in Kardinal is like a “branch” of a deployed application on Kubernetes. You only deploy what you need, streamlining overhead and reducing cloud costs by over 90%—especially beneficial for larger teams and complex microservice architectures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fsn78euzyblbznm60x9vc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fsn78euzyblbznm60x9vc.png" alt="Screen Shot 2024-10-04 at 1.00.15 PM.png" width="800" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll combine the strengths of developing on Tilt, and the efficient service routing of Kardinal. You'll learn how to effectively deploy your applications, manage resources, and build containers while ensuring a smooth and efficient development experience.&lt;/p&gt;

&lt;p&gt;Curious to dive deeper? Check out our &lt;a href="https://github.com/kurtosis-tech/kardinal?ref=blog.kardinal.dev" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; for more info on Kardinal, and don’t miss the &lt;a href="https://github.com/kurtosis-tech/new-obd/blob/main/Tiltfile" rel="noopener noreferrer"&gt;Tiltfile in the Kardinal Boutique demo app&lt;/a&gt; for a hands-on experience.&lt;/p&gt;

&lt;p&gt;Ready to transform your development process? Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we dive in, make sure you have the following set up first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://github.com/kurtosis-tech/kardinal?tab=readme-ov-file#installation" rel="noopener noreferrer"&gt;Kardinal CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://docs.tilt.dev/install.html" rel="noopener noreferrer"&gt;Tilt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  A local Kubernetes (K8s) cluster (you can use &lt;a href="https://minikube.sigs.k8s.io/docs/start" rel="noopener noreferrer"&gt;Minikube&lt;/a&gt; or &lt;a href="https://docs.docker.com/desktop/kubernetes/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;)

&lt;ul&gt;
&lt;li&gt;⛳ &lt;strong&gt;flag&lt;/strong&gt;: If you’re new to Kardinal, we suggest trying it locally first, before using it with your remote clusters.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://istio.io/latest/docs/setup/install/istioctl/#install-istio-using-the-default-profile" rel="noopener noreferrer"&gt;Istio&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;💡&lt;strong&gt;flag:&lt;/strong&gt; It’s not important that your cluster uses Istio. Kardinal will manage everything related to Istio on your development cluster.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  Distributed tracing on your application

&lt;ul&gt;
&lt;li&gt;For a full list of supported distributed tracing systems, check out &lt;a href="https://kardinal.dev/docs/getting-started/own-app" rel="noopener noreferrer"&gt;our docs here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up Kardinal for your application
&lt;/h2&gt;

&lt;p&gt;Getting started with Kardinal involves deploying Kardinal Manager to your cluster, and annotating your kubernetes manifest. Don’t worry, this should only take few minutes!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  If you haven’t installed Kardinal yet, run the following command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;curl&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kardinal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  Lets set up a local development cluster for testing first. All you need is a cluster with Istio installed, with kubectl pointing to your cluster. If you don’t have one handy, not to worry!We can get set up by installing the below on your machine:Then running the following:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://minikube.sigs.k8s.io/docs/start/" rel="noopener noreferrer"&gt;Minikube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" rel="noopener noreferrer"&gt;Kubectl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://istio.io/latest/docs/setup/getting-started/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;disk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;
&lt;span class="n"&gt;minikube&lt;/span&gt; &lt;span class="n"&gt;addons&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="n"&gt;ingress&lt;/span&gt;
&lt;span class="n"&gt;minikube&lt;/span&gt; &lt;span class="n"&gt;addons&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="n"&gt;istioctl&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  This is normally when you would deploy Kardinal Manager to your cluster, however in this case, we’ll be using Tilt for this so you can skip for now.&lt;/li&gt;
&lt;li&gt;  We’ll then get started by having Kardinal recognize your application’s entrypoint. Kardinal supports managing access to your services either through the Kubernetes Ingress API or the Gateway API.All you need is to add the &lt;code&gt;kardinal.dev.service/ingress&lt;/code&gt; or &lt;code&gt;kardinal.dev.service/gateway&lt;/code&gt; annotations to your Ingress or Gateway manifest to mark this entrypoint.You can find more information and example annotations &lt;a href="https://kardinal.dev/docs/getting-started/own-app" rel="noopener noreferrer"&gt;in our docs here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re all set! Now to move onto the fun part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: deploy your current Kardinal topology
&lt;/h3&gt;

&lt;p&gt;If you've already deployed your application's manifest using the &lt;code&gt;kardinal deploy&lt;/code&gt; command, your cluster topology is ready for deployment with Tilt. If you haven’t, check out our getting started docs &lt;a href="https://kardinal.dev/docs/getting-started/install" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the command&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tilt up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a&lt;/strong&gt; &lt;code&gt;Tiltfile&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kardinal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;topology&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;print-manifest&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--add-trace-router&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;quiet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kardinal_topology_yaml_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kardinal_topology_yaml_str&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;k8s_yaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;local_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ingress-gateway-port-forward&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;serve_cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kubectl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;port-forward&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service/istio-ingressgateway&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;80:80&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;istio-system&lt;/span&gt;&lt;span class="sh"&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;In this setup, the first &lt;code&gt;local&lt;/code&gt; call retrieves the cluster topology from Kardinal Kontrol using the &lt;code&gt;kardinal topology&lt;/code&gt; command. This generates a multi-resource manifest that Tilt applies with the &lt;code&gt;k8s_yaml&lt;/code&gt; command. The &lt;code&gt;local_resource&lt;/code&gt; function then executes the port forwarding command, allowing the Ingress Gateway to handle browser requests on port 80.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: deploy your manifest file with Kardinal annotations
&lt;/h3&gt;

&lt;p&gt;You can also incorporate the &lt;code&gt;kardinal deploy&lt;/code&gt; command within Tilt to manage the deployment flow seamlessly. Here’s how:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the command&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tilt up

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a&lt;/strong&gt; &lt;code&gt;Tiltfile&lt;/code&gt; like this, replacing the placeholder data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kardinal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-k&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{your-kardinal-manifest-yaml-filepath}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kardinal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;topology&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;print-manifest&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--add-trace-router&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;quiet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kardinal_topology_yaml_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kardinal_topology_yaml_str&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;k8s_yaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;local_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ingress-gateway-port-forward&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;serve_cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kubectl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;port-forward&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service/istio-ingressgateway&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;80:80&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;istio-system&lt;/span&gt;&lt;span class="sh"&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;In this example, the &lt;code&gt;kardinal deploy&lt;/code&gt; command is used at the beginning to deploy your multi-resource manifest file directly to Kardinal Kontrol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build and deploy your application
&lt;/h2&gt;

&lt;p&gt;Integrate Kardinal with Tilt to build your application containers and set up workflows that automatically reflect changes when you save your files. Follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the command&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tilt up

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a&lt;/strong&gt; &lt;code&gt;Tiltfile&lt;/code&gt; like this, replacing the placeholder data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kardinal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-k&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{your-kardinal-manifest-yaml-filepath}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kardinal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;flow&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{service}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{service-dev-image}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;docker_build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{service-dev-image}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{./src/}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dockerfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{./src/Dockerfile}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kardinal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;topology&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;print-manifest&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--add-trace-router&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;quiet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kardinal_topology_yaml_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kardinal_topology_yaml_str&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;k8s_yaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kardinal_topology_yaml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_duplicates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;local_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tunnel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;serve_cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;minikube&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tunnel&lt;/span&gt;&lt;span class="sh"&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;In this setup, we introduce two new elements: the &lt;code&gt;kardinal flow&lt;/code&gt; local execution for creating the development flow and the &lt;code&gt;docker_build&lt;/code&gt; function to link the container being built with the service in the development flow. This setup triggers Tilt's hot reload mechanism whenever changes are made to files within the specified context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing PROD and DEV flows
&lt;/h2&gt;

&lt;p&gt;Once your Tiltfile execution is complete, you’ll see URLs for both the &lt;code&gt;prod&lt;/code&gt; and &lt;code&gt;dev&lt;/code&gt; applications printed in the logs. Click on these links to access the respective flows in your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxusy3aw30zl7qy166w1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxusy3aw30zl7qy166w1j.png" width="274" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F4iz1x67kmuxtj9jakgyl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F4iz1x67kmuxtj9jakgyl.png" width="362" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;After finishing your development cycle, you can remove all created resources from the cluster by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tilt down &lt;span class="nt"&gt;--delete-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--delete-namespaces&lt;/code&gt; flag will remove your application’s namespace, but it won’t affect the &lt;code&gt;default&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;Once you feel comfortable with running this setup locally, you can deploy to remote clusters as needed, enjoying the benefits of both Tilt’s devex and Kardinal’s efficient dev flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful resources
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Kardinal, get additional support, or test it out without installing anything-here are some helpful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Explore our &lt;a href="https://kardinal.dev/docs?ref=blog.kardinal.dev" rel="noopener noreferrer"&gt;docs&lt;/a&gt; to learn more about how Kardinal works.&lt;/li&gt;
&lt;li&gt;  Ask questions and get help in our community &lt;a href="https://discuss.kardinal.dev/?ref=blog.kardinal.dev" rel="noopener noreferrer"&gt;forum&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  Test Kardinal without installing via our &lt;a href="https://github.com/kurtosis-tech/kardinal-playground?ref=blog.kardinal.dev" rel="noopener noreferrer"&gt;playground&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  For additional examples of configuring Kardinal with Tilt, check out the &lt;a href="https://github.com/kurtosis-tech/new-obd/blob/main/Tiltfile" rel="noopener noreferrer"&gt;Tiltfile in the Kardinal Boutique demo app&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! 💻&lt;/p&gt;

</description>
      <category>devops</category>
      <category>productivity</category>
      <category>k8s</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Building the lightest-weight Kubernetes dev ephemeral environments</title>
      <dc:creator>TC Ricks</dc:creator>
      <pubDate>Thu, 22 Aug 2024 15:17:03 +0000</pubDate>
      <link>https://dev.to/tc87/building-the-lightest-weight-kubernetes-dev-ephemeral-environments-3c5l</link>
      <guid>https://dev.to/tc87/building-the-lightest-weight-kubernetes-dev-ephemeral-environments-3c5l</guid>
      <description>&lt;p&gt;We’re excited to introduce &lt;a href="https://github.com/kurtosis-tech/kardinal" rel="noopener noreferrer"&gt;Kardinal&lt;/a&gt;, an open-source tool that’s designed to make development and test environments for Kubernetes-deployed applications as lightweight as possible. If you manage multiple application deploys across dev, test, and QA, or you’re spinning up expensive development sandboxes, Kardinal can cut down resource usage and time-to-test by over 90%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kardinal makes development easier
&lt;/h2&gt;

&lt;p&gt;Testing new features often means spinning up dedicated application environments, which can be both costly and resource-draining. Kardinal flips the script by letting you create temporary, tailored environments that are dynamically managed. This means you get to focus on what really matters without the hassle of managing extra overhead.&lt;/p&gt;

&lt;p&gt;Kardinal redefines development environments by introducing the concept of "&lt;a href="https://kardinal.dev/docs/concepts/flows" rel="noopener noreferrer"&gt;flows&lt;/a&gt;": dedicated paths through your Kubernetes cluster that act as ephemeral environments. These flows are created on-demand to deploy and test the specific versions of services relevant to feature development. In traditional development setups, managing and testing new features often involves deploying numerous resources that other developers have already deployed in some form or fashion, leading to inefficiencies and high resource usage.&lt;/p&gt;

&lt;p&gt;These inefficiencies are addressed by deploying the minimum necessary resources related to the feature under development, and reusing any other resource that’s already been deployed in another workflow. This strategy optimizes resource usage. Whether you need a single-service test or a full application flow with isolated state, Kardinal’s flexible architecture ensures you have the tools to test and develop efficiently.&lt;/p&gt;

&lt;p&gt;Some of the benefits of this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral environments&lt;/strong&gt;: Spin up a new environment exactly when you need it, and just as quickly spin it down when you’re done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal resource usage&lt;/strong&gt;: Only deploy the services you’re actively working on. Kardinal takes care of the rest, so you don’t waste resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible flow types&lt;/strong&gt;: Whether you need to test a single service or an entire application, Kardinal has you covered:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-service flows&lt;/strong&gt;: Perfect for when you’re tweaking just one service.

&lt;ul&gt;
&lt;li&gt;Multi-service flows: Ideal for when your feature involves multiple services.&lt;/li&gt;
&lt;li&gt;State-isolated flows: Great for features that need their own databases or caches.&lt;/li&gt;
&lt;li&gt;Full application flows: For those times when you need end-to-end testing with full isolation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Cost savings&lt;/strong&gt;: Kardinal can help you save big by avoiding unnecessary resource duplication. Check out the &lt;a href="https://kardinal.streamlit.app/" rel="noopener noreferrer"&gt;cost calculator here&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Open source&lt;/strong&gt;: Kardinal is open source, so use it however fits best within your workflows&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Kardinal works
&lt;/h2&gt;

&lt;p&gt;Kardinal is designed to streamline dev and testing environments for your Kubernetes-deployed application.  &lt;/p&gt;

&lt;p&gt;Here’s a quick look at how it operates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kardinal.dev%2Fcontent%2Fimages%2F2024%2F08%2FScreenshot-2024-08-20-at-3.06.55-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kardinal.dev%2Fcontent%2Fimages%2F2024%2F08%2FScreenshot-2024-08-20-at-3.06.55-PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Kardinal manager + Kardinal kontrol (control plane)
&lt;/h3&gt;

&lt;p&gt;At the heart of Kardinal are Kardinal kontrol and Kardinal manager. Kardinal kontrol is a cloud-hosted control plane that maintains information about topology and resources in your cluster. Kardinal manager is deployed as a service inside your cluster. It maintains a live cluster topology from the control plane and uses routing rules with Istio, to ensure that traffic is routed properly within your environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing state with plugins
&lt;/h3&gt;

&lt;p&gt;To handle state in your cluster, Kardinal uses a flexible plugin system. These plugins help manage and share state across different environments or set up new “dev” versions of stateful services as needed. Here’s a breakdown:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kardinal.dev%2Fcontent%2Fimages%2F2024%2F08%2FUntitled--8--1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.kardinal.dev%2Fcontent%2Fimages%2F2024%2F08%2FUntitled--8--1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Stateful Service Plugins&lt;/strong&gt;: These are for managing state in services running on Kubernetes, like Postgres databases.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;External Service Plugins&lt;/strong&gt;: These handle state for external services not managed by Kubernetes, such as Stripe or Twilio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using these plugins, Kardinal can efficiently set up and tear down flows, reusing existing state or creating new instances as needed.&lt;/p&gt;

&lt;p&gt;Plugins operate by applying patches on top of your baseline Kubernetes manifest, which appropriately modify the deployment of the stateful service, or services that depend on external services, for the new flow. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You can modify a microservice deployment spec to change an API key environment variable to access a separate dev account for an external service.&lt;/li&gt;
&lt;li&gt;  You can also run scripts within plugins to do things like provision new state, for example to deploy a dev database seeded with data for testing purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Visualizing a cluster
&lt;/h3&gt;

&lt;p&gt;Kardinal includes a frontend that provides a visual representation of your cluster. This frontend connects to the Kardinal manager to retrieve info, show service dependencies, and manage multi-user scenarios. Currently, the control plane is cloud-hosted, but an on-premise option is in the works.&lt;/p&gt;

&lt;p&gt;You can read more about our high level concepts and architecture in &lt;a href="https://kardinal.dev/docs" rel="noopener noreferrer"&gt;our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Kardinal
&lt;/h2&gt;

&lt;p&gt;Getting started with Kardinal is straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Kardinal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To install Kardinal, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl get.kardinal.dev -sL | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Set up a development Kubernetes cluster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All you need is a Kubernetes cluster with Istio enabled, and kubectl installed on your machine, pointing to your cluster. If you need help with this, read more &lt;a href="https://kardinal.dev/docs/getting-started/install" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Deploy the Kardinal Manager to your cluster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure that kubectl is pointing to your cluster, and then run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kardinal manager deploy kloud-kontrol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, follow the demo below, or check out our docs on &lt;a href="https://kardinal.dev/docs/getting-started/own-app" rel="noopener noreferrer"&gt;creating your first flow&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s dive into a demo
&lt;/h2&gt;

&lt;p&gt;If you want to see Kardinal in action, here’s how to get started with a demo environment using an online boutique store as an example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Deploy the demo app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a new terminal window, deploy the demo app via Kardinal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://raw.githubusercontent.com/kurtosis-tech/new-obd/main/release/obd-kardinal.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./obd-kardinal.yaml
kardinal deploy &lt;span class="nt"&gt;-k&lt;/span&gt; ./obd-kardinal.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up the gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kardinal gateway prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can view the frontend of the demo app by going to the local host link that the gateway prints.&lt;/p&gt;

&lt;p&gt;Feel free to click around, add items to your cart, and shop!&lt;/p&gt;

&lt;p&gt;If you want to see the architecture of your application, the Kardinal dashboard will show you it along with any logical environments (flows) you create on top of it.&lt;/p&gt;

&lt;p&gt;To view the dashboard, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kardinal dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click "Traffic configuration" in the sidebar to see the architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create a lightweight development environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's deploy a dev version of the frontend that's adding bolder style to the website, this modification is contained into a single &lt;code&gt;frontend&lt;/code&gt; image using an image we've prepared for this demo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kardinal flow create frontend kurtosistech/frontend:demo-frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will output a URL that you can use to access the frontend of the development environment. You can view the frontend of the application by going to the URL provided.&lt;/p&gt;

&lt;p&gt;To interact with the dev version, first stop your previous gateway (if it's still running)--currently you can only run one gateway at a time in this demo, and run the following to forward the dev demo application port to a URL you can access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; kardinal gateway &amp;lt;flow-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that there are already items in your cart in the development environment. We've configured the development "flow" in this demo to run with it's own database which is seeded with test data.  &lt;/p&gt;

&lt;p&gt;To inspect the resources in your cluster, and see how Kardinal is reusing resources from your stable environment in the dev environment, go to the dashboard again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kardinal dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and click on the "Traffic configuration" sidebar item.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Clean up your development flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The flow_id is in the output of the kardinal flow create command, but if you've lost it, you can get it again by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kardinal flow &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you're done with your development flow, you can delete it by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kardinal flow delete &amp;lt;flow_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you're done! 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful resources
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Kardinal, get additional support, or test it out without installing anything—here are some helpful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Explore our &lt;a href="https://kardinal.dev/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; to learn more about how Kardinal works.&lt;/li&gt;
&lt;li&gt;  Ask questions and get help in our community &lt;a href="https://discuss.kardinal.dev/" rel="noopener noreferrer"&gt;forum&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  Test Kardinal without installing via our &lt;a href="https://github.com/kurtosis-tech/kardinal-playground" rel="noopener noreferrer"&gt;playground&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>k8s</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Introducing Streamlit Components</title>
      <dc:creator>TC Ricks</dc:creator>
      <pubDate>Fri, 17 Jul 2020 19:06:15 +0000</pubDate>
      <link>https://dev.to/tc87/introducing-streamlit-components-31e0</link>
      <guid>https://dev.to/tc87/introducing-streamlit-components-31e0</guid>
      <description>&lt;p&gt;&lt;em&gt;This originally appeared on July 14th, 2020 by Adrien Treuille. &lt;a href="(https://medium.com/streamlit/introducing-streamlit-components-d73f2092ae30)"&gt;Click here to see the original content&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.youtube.com/watch?v=CmSKVW1v0xM" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L7_Ve3kp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://img.youtube.com/vi/CmSKVW1v0xM/0.jpg" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;In the ten months since Streamlit was released, the community has created over 250,000 apps for everything from &lt;a href="http://adilmoujahid.com/posts/2020/06/streamlit-messi-ronaldo/" rel="noopener noreferrer"&gt;analyzing soccer games&lt;/a&gt; to &lt;a href="https://github.com/TKassis/OrgaQuant" rel="noopener noreferrer"&gt;measuring organoids&lt;/a&gt;, and from &lt;a href="http://predictivehealthcare.pennmedicine.org/2020/03/14/accouncing-chime.html" rel="noopener noreferrer"&gt;COVID-19 tracking&lt;/a&gt; to &lt;a href="https://huggingface.co/zero-shot/" rel="noopener noreferrer"&gt;zero-shot topic classification&lt;/a&gt;. Inspired by your creativity, we added &lt;a href="https://github.com/streamlit/streamlit/pull/488" rel="noopener noreferrer"&gt;file uploaders&lt;/a&gt;, &lt;a href="https://github.com/streamlit/streamlit/pull/1325" rel="noopener noreferrer"&gt;color pickers&lt;/a&gt;, &lt;a href="https://github.com/streamlit/streamlit/pull/1483" rel="noopener noreferrer"&gt;date ranges&lt;/a&gt;, and other features. But as the complexity of serving the community grew, we realized that we needed a more scalable way to grow Streamlit's functionality. So we're turning to Streamlit's best source of ideas: you!&lt;/p&gt;

&lt;p&gt;Today, we are excited to announce &lt;em&gt;Streamlit Components,&lt;/em&gt; the culmination of a multi-month project to enable the Streamlit community to create and share bits of functionality. Starting in Streamlit version 0.63, you can tap into the component ecosystems of React, Vue, and other frameworks. Create new widgets with custom styles and behaviors or add new visualizations and charting types. The possibilities are endless!&lt;/p&gt;

&lt;h3&gt;
  
  
  The Streamlit Components Gallery
&lt;/h3&gt;

&lt;p&gt;The first thing you should do is check out the &lt;a href="http://streamlit.io/components" rel="noopener noreferrer"&gt;Streamlit Components Gallery&lt;/a&gt; to see what others have created and shared.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHHujtED--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AiVJmnUvv83cM6HvJ2NcZ9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHHujtED--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AiVJmnUvv83cM6HvJ2NcZ9g.png" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each component can be installed with just a single line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install some_cool_component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't find a component that works for you, you can make your own!&lt;/p&gt;

&lt;h3&gt;
  
  
  Building your own components
&lt;/h3&gt;

&lt;p&gt;Streamlit has a unique, functional style which lets you create rich, interactive experiences in very few lines of code. For example, let's check out this simple Streamlit app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;` squared is `&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mbxHfdXy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AhvNO9Y68zvwqoQmgHbaqkQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mbxHfdXy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AhvNO9Y68zvwqoQmgHbaqkQ.png" width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at this code, you can see that Streamlit calls come in two flavors: &lt;strong&gt;static components&lt;/strong&gt; like &lt;code&gt;st.markdown&lt;/code&gt; are stateless and only send data &lt;em&gt;to&lt;/em&gt; the browser, whereas &lt;strong&gt;bidirectional components&lt;/strong&gt; like &lt;code&gt;st.slider&lt;/code&gt; have internal state and send data &lt;em&gt;back from&lt;/em&gt; the browser.&lt;/p&gt;

&lt;p&gt;Our challenge was to provide an API that embraces Streamlit's functional style while capturing these use-cases as simply as possible. A few months ago, two amazing Streamlit engineers, Tim Conkling and Henrikh Kantuni, tackled this challenge and came up with a super elegant solution. The result is the new &lt;code&gt;streamlit.components.v1&lt;/code&gt; package which comprises three functions. For static components, we added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;html(...)&lt;/code&gt;, which lets you build components out of HTML, Javascript, and CSS&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;iframe(...)&lt;/code&gt; , which lets you embed external websites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For bidirectional components, we added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;declare_component(...)&lt;/code&gt;, which lets you build interactive widgets which bidirectionally communicate between Streamlit and the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive into how it works!&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Components
&lt;/h3&gt;

&lt;p&gt;Let's start with a simple &lt;strong&gt;static component&lt;/strong&gt; to embed snippets of code called &lt;em&gt;Github gists&lt;/em&gt; in your app. Ideally, adding the component should just be a single function call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Render a gist
github_gist('tc87', '9382eafdb6eebde0bca0c33080d54b58')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which would render a gist like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zqUvJ5V8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2ArUEG-BKT4aM1khaEYSgvog.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqUvJ5V8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2ArUEG-BKT4aM1khaEYSgvog.png" width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create such a component, we start by importing the Streamlit Components library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit.components.v1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This somewhat wordy import statement serves two purposes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It versions the components API so that future changes don't break existing components.&lt;/li&gt;
&lt;li&gt; It reminds us that we're starting to use deep magic which we should hide from the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now let's use the &lt;code&gt;html(...)&lt;/code&gt; method to serve up the gist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;github_gist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gist_creator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gist_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scrolling&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
      &amp;lt;script src=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gist.github.com/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gist_creator&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gist_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.js&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;
      &amp;lt;/script&amp;gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;scrolling&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;scrolling&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;This approach has a few awesome properties. First, it's really simple and functional. In fact, this pattern lets you hide the ugly-looking HTML and wrap it into a pretty, Pythonic function call, &lt;code&gt;github_gist(...)&lt;/code&gt;. You can wrap code in a function and reuse it throughout your project. (Better yet, put it in a package and share it with the community in the gallery.) Second, note that we can add arbitrary HTML in this component --- &lt;code&gt;div&lt;/code&gt;s, &lt;code&gt;span&lt;/code&gt;s, and yes, &lt;code&gt;script&lt;/code&gt;s! We can do this safely because the component is sandboxed in an &lt;code&gt;iframe&lt;/code&gt; which lets us include external scripts without worrying about security problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting widget with it!
&lt;/h3&gt;

&lt;p&gt;What if you want to create a stateful &lt;strong&gt;bidirectional component&lt;/strong&gt; that passes information back to Python from the browser, or in other words, a &lt;em&gt;widget&lt;/em&gt;? You can do this too! For example, let's create a counter component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fanilo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The count is&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which creates this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--opCc4BCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AOiHcNHh-zVYRF6MHr9wT0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--opCc4BCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1600/1%2AOiHcNHh-zVYRF6MHr9wT0w.png" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that this code follows Streamlit's unique functional style and captures the counter &lt;em&gt;state&lt;/em&gt; embedded in the component. How did we achieve this? Happily, a single function call, &lt;code&gt;declare_component&lt;/code&gt;, does all the work to enable bidirectional communication with Streamlit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Declare a simple counter component.import 
&lt;/span&gt;&lt;span class="n"&gt;streamlit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;componentscounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;declare_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;counter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BUILD_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! Under the hood, &lt;code&gt;BUILD_PATH&lt;/code&gt; points to a component built using React, Vue, or any frontend technology you like. For this example we decided to use React and Typescript giving us this render function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;Hello&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&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="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&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;onClicked&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&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;Click&lt;/span&gt; &lt;span class="nx"&gt;Me&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;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;and this callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;onClicked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;prevState&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;numClicks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numClicks&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;Streamlit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setComponentValue&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numClicks&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;Donezo! You've now created a simple, stateful component which "feels like React" on the website, and "feels like Streamlit" on the Python side. Information is passed back to Python using &lt;code&gt;Streamlit.setComponentValue(...)&lt;/code&gt;. Because we're using React in this case, the component's state is stored in &lt;code&gt;this.state&lt;/code&gt;. For more details on this example, see our &lt;a href="http://github.com/streamlit/component-template" rel="noopener noreferrer"&gt;component template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A neat benefit of this architecture is that you're not limited to React. You can use any language or framework which compiles for the web. &lt;a href="https://github.com/kantuni/streamlit-meets-cljs" rel="noopener noreferrer"&gt;Here is the same counter component&lt;/a&gt; written in ClojureScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defonce&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;increment-counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;swap!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;send-message-to-streamlit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:set-component-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;:button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:on-click&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;increment-counter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Click Me!"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another cool feature of this API is that you can do hot-reloading as you develop your component like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;declare_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:3001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the &lt;code&gt;url&lt;/code&gt; parameter lets you specify a dev server for the component &lt;a href="https://github.com/streamlit/component-template#quickstart" rel="noopener noreferrer"&gt;created with&lt;/a&gt; &lt;code&gt;[npm run start](https://github.com/streamlit/component-template#quickstart)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What we've shown you so far just scratches the surface. For more details, &lt;a href="https://docs.streamlit.io/en/stable/streamlit_components.html" rel="noopener noreferrer"&gt;please check our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharing with the world
&lt;/h3&gt;

&lt;p&gt;Did you create something broadly useful for the Streamlit community? Sure, you could keep that superpower for yourself, but it would be even cooler to share it! Get community feedback and praise. 😇 You can easily wrap your component in a PyPi package and &lt;a href="https://docs.streamlit.io/en/stable/publish_streamlit_components.html#publish-streamlit-component-to-pypi" rel="noopener noreferrer"&gt;submit it to our Gallery by following these instructions&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it out and let us know what you think!
&lt;/h3&gt;

&lt;p&gt;We're excited to unlock for the community a new way to plug-and-play functionality into Streamlit. Streamlit Components let you write simple HTML extensions or tap into the whole ecosystem provided by React, Vue, and other frameworks. Your feedback drives innovation in Streamlit. Please tell us what you think and what you'd like next. Show off your shiny new components and share them with the world. We can't wait to see what you build! 🎈&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Special thanks to Fanilo Andrianasolo, Daniel Haziza, Synode, and the entire Streamlit Components beta community who helped refine this architecture and inspired us with their feedback and ideas. Thanks also to TC Ricks, Amanda Kelly, Thiago Teixeira, Beverly Treuille, Regan Carey, and Cullan Carey for their input on this article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
