<?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>Forem: develaper</title>
    <description>The latest articles on Forem by develaper (@develaper).</description>
    <link>https://forem.com/develaper</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%2F1287241%2Fe6b32a0e-1cf1-4eac-83a7-efdc37081d92.jpeg</url>
      <title>Forem: develaper</title>
      <link>https://forem.com/develaper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/develaper"/>
    <language>en</language>
    <item>
      <title>10 AI terms that will help you look like you're not completely lost</title>
      <dc:creator>develaper</dc:creator>
      <pubDate>Mon, 29 Dec 2025 13:15:31 +0000</pubDate>
      <link>https://forem.com/develaper/10-ai-terms-that-will-help-you-look-like-youre-not-completely-lost-2648</link>
      <guid>https://forem.com/develaper/10-ai-terms-that-will-help-you-look-like-youre-not-completely-lost-2648</guid>
      <description>&lt;p&gt;In just over a decade, we’ve gone from talking about AI as a science-fiction concept—something only Sarah Connor took seriously—to having it almost by default, even in our electric toothbrushes. And if you don’t see it that way, I hope you’ve got a self-sufficient farm running somewhere, because this is the direction the software industry is heading, and denying it won’t make it disappear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dog years&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One year in the human world is roughly equivalent to five years when it comes to AI-related technologies. If you started using an AI-based tool in a project a couple of years ago, chances are that the technology you relied on is now completely obsolete. New tools and paradigms emerge every day, leaving behind concepts that were considered revolutionary and innovative just a few months ago.&lt;/p&gt;

&lt;p&gt;Staying on top of things is a must in this industry. It’s no secret that you need one eye on the latest updates of the stack you’re working with, and another on the new technologies gaining traction, if you don’t want to fall out of the game. But this pace is dizzying even for those of us who are just trying to make a living—and pay our streaming subscriptions—in the software industry. That’s why I’ve tried to put together a short list of terms that might be helpful if, like me, you have the feeling that this train is leaving without you (the paradox being that this list will very likely be outdated by the time you finish reading it).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1-Agentic AI:&lt;/strong&gt;&lt;br&gt;
This is the top one, no discussion here. These days, unless you’ve been living under a rock, you’ve probably noticed that almost everyone is building something that claims to involve AI agents. This is a good moment to pause for a second and clarify terms: what is an AI agent, really?&lt;/p&gt;

&lt;p&gt;Let’s start with what it is not. An AI agent is not a chatbot. A chatbot responds to a prompt and stops there. An agent, by contrast, operates in a more autonomous way: it can receive information from its environment (inputs, state, APIs), reason about it, decide which action to take, and execute it. It then observes the outcome and, if necessary, repeats the cycle.&lt;/p&gt;

&lt;p&gt;This perceive–reason–act–observe loop, together with some degree of memory and tool usage, is what differentiates an agent from a simple conversation with a language model. This approach can be applied to almost any programmatically definable context—from travel agency workflows to data analysis or DevOps engineering tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2-Large Reasoning Model:&lt;/strong&gt;&lt;br&gt;
Many agents rely on language models optimized for reasoning tasks, often informally referred to as Large Reasoning Models. Before going deeper, it’s worth clarifying what they are—and what they are not.&lt;/p&gt;

&lt;p&gt;In most commercial chatbots, one of the main priorities is to deliver fast responses. This works well for simple tasks, but as problems become more complex, multiple iterations are often required to reach a satisfactory result.&lt;/p&gt;

&lt;p&gt;In some cases, the system takes longer to generate a response, which users usually perceive as the familiar “thinking…” message. This doesn’t mean a different model is being used, but rather that the model is producing a longer output or following a more elaborate internal reasoning process.&lt;/p&gt;

&lt;p&gt;When we talk about Large Reasoning Models, we generally mean models that are better tuned to break problems down into intermediate steps, maintain coherence throughout the reasoning process, and handle tasks that require planning. This kind of behavior—whether emerging from the model itself or supported by external logic—is exactly what AI agents need to operate effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3-Vector Database:&lt;/strong&gt;&lt;br&gt;
If you’ve ever spent even a moment thinking about AI from a technical perspective, you’ve probably asked yourself this question: where is all this information stored? And it’s not a trivial question.&lt;/p&gt;

&lt;p&gt;Even without a deep understanding of how LLMs work internally, it’s easy to see that the real challenge isn’t storing data—traditional databases are still perfectly fine for that—but rather being able to search and relate information by meaning, not just by exact matches.&lt;/p&gt;

&lt;p&gt;Before diving into vector databases, we need to introduce two more key concepts: embedding models and vectors.&lt;/p&gt;

&lt;p&gt;**4-EMbeding Model: An embedding model is responsible for transforming data such as text, images, or audio into numerical vectors. These vectors are mathematical representations that allow us to capture similarity relationships between different pieces of data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5-What is a vector?&lt;/strong&gt;&lt;br&gt;
A vector is essentially a list of numbers that represents an object within a high-dimensional mathematical space. While individual dimensions are usually not directly interpretable, the distance between vectors allows us to measure how semantically similar two elements are.&lt;/p&gt;

&lt;p&gt;This is where vector databases come into play: they make it possible to perform mathematical operations to find nearby vectors, which translates into searching and working with semantically similar content.&lt;/p&gt;

&lt;p&gt;For example, in a traditional database, an image is typically stored as a BLOB (Binary Large Object), which allows us to store and retrieve it but tells us nothing about its content. In a vector database, that same image is processed using an embedding model to generate a numerical vector.&lt;/p&gt;

&lt;p&gt;As a simplification, we can imagine some dimensions representing concepts like “landscape,” “mountains,” or “people.” In practice, these dimensions are neither explicit nor human-readable, but the result is the same: images with similar content end up close to each other in the vector space, making search and comparison much easier.&lt;/p&gt;

&lt;p&gt;That, essentially, is how vector databases work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. RAG (Retrieval Augmented Generation):&lt;/strong&gt;&lt;br&gt;
This is where things get interesting, because RAG brings together many of the concepts we’ve covered so far. At its core, RAG is a technique that enriches an LLM’s generation with relevant external information retrieved dynamically at runtime.&lt;/p&gt;

&lt;p&gt;The process typically works as follows: when a user submits a prompt, it is passed to a component called the retriever. The retriever converts the query into a vector using an embedding model and performs a similarity search against a vector database. Rather than returning a single result, it usually retrieves multiple relevant chunks of information.&lt;/p&gt;

&lt;p&gt;These chunks—now in plain text, not vectors—are then injected into the prompt sent to the LLM. This allows the model to generate responses grounded in specific, up-to-date, or private context, without having been trained on that information beforehand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. MCP (Model Context Protocol):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For an LLM to be truly useful, it needs to be able to interact with external resources. It’s not enough for it to generate text in isolation; it must connect to databases, services, and other tools. MCP is a pattern that defines how information from these sources is managed and channeled to the LLM.&lt;/p&gt;

&lt;p&gt;The MCP server acts as an intermediary between the model and external services—such as databases or email systems—so developers don’t have to reinvent the connection every time they want the LLM to access a new resource.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8.MOE (Mixture of Experts):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MOE splits an LLM into multiple specialized subnetworks, called “experts.” A routing mechanism decides which of these experts to activate for a given task, so that only the necessary ones are used. Each expert produces an output that is then combined, usually through a weighted sum determined by the routing mechanism.&lt;/p&gt;

&lt;p&gt;This architecture allows scaling to models with billions of parameters without consuming all the model’s resources at each step, significantly optimizing efficiency and performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9.Agentic RAG:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the concept of RAG is already somewhat abstract, Agentic RAG takes it a step further. While traditional RAG has simple retrieval, limited adaptability, and relies on static knowledge, Agentic RAG incorporates AI agents that can decide which tools to use, formulate retrieval strategies, and refine queries for more accurate and flexible responses.&lt;/p&gt;

&lt;p&gt;At a high level, the workflow is as follows:&lt;br&gt;
1- The user query is directed to an AI agent for processing.&lt;br&gt;
2- The agent uses short-term and long-term memory to track query context, defines a retrieval strategy, and selects the most appropriate tools.&lt;br&gt;
3- The data fetching process can use tools such as vector search, multiple agents, or MCP servers to gather relevant information from the knowledge base.&lt;br&gt;
4- The agent combines the retrieved data with the query and system prompt and passes it to the LLM.&lt;br&gt;
5- The LLM processes this optimized input to generate a response to the user’s query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. ASI (Artificial Super Intelligence):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And this is where one might be tempted to throw the computer out the window and consider a career as a farmer. The concept of ASI, as far as I know, is more theoretical than practical, but it exists and is important to keep in mind, especially given the speed at which technology evolves.&lt;/p&gt;

&lt;p&gt;All the tools and concepts described so far, which we’re somewhat familiar with, slowly approach the paradigm of AGI (Artificial General Intelligence). Simplifying, this means systems capable of performing any task at the level of a human expert. ASI goes a step further, implying a system with a much broader scope, capable of self-improvement, solving problems “better” than a human expert, and even posing problems we cannot yet imagine.&lt;/p&gt;

&lt;p&gt;And so, between autonomous agents, databases that “understand” your photos, architectures that decide which expert to activate, and the ever-elusive promise of ASI, it’s hard not to feel a bit overwhelmed… and utterly fascinated at the same time. The good news is, you don’t need to be a guru to hop on this train: all it takes is curiosity, patience, and a strong cup of coffee. Who knows, maybe in a few years my toaster will have its own RAG agent and recommend the perfect breakfast recipe while I’m still trying to figure out what my LLM is doing.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>learning</category>
      <category>llm</category>
    </item>
    <item>
      <title>Two wrongs doesn't make a Polymorphic.</title>
      <dc:creator>develaper</dc:creator>
      <pubDate>Sat, 25 Oct 2025 05:41:45 +0000</pubDate>
      <link>https://forem.com/develaper/two-wrongs-doesnt-make-a-polymorphic-5503</link>
      <guid>https://forem.com/develaper/two-wrongs-doesnt-make-a-polymorphic-5503</guid>
      <description>&lt;h2&gt;
  
  
  🐎 Another episode in the everyday adventures of a cowboy coder in rehab
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/develaper/polymorphic-relationships-matter-and-my-adhd-agrees-236f"&gt;In my previous post&lt;/a&gt;, I told you about that assignment that landed on my desk a few days ago. I talked about how I started modeling the database for a content API that came with some rather misleading requirements.&lt;/p&gt;

&lt;p&gt;In today’s episode, we’re going to dig a little deeper into one of the common characteristics shared by all content types.&lt;br&gt;&lt;br&gt;
Our streaming content API had big international ambitions. It was ready to conquer every platform in every country—and it wasn’t going to let a third-rate developer like me get in the way.&lt;/p&gt;

&lt;p&gt;All the content types I modeled in the previous post had one thing in common: the &lt;code&gt;availability&lt;/code&gt; property.&lt;br&gt;&lt;br&gt;
Here, I thought I could go for the simplest possible solution, right?&lt;br&gt;&lt;br&gt;
Well, said and almost done: availability turned out to be a hash with just two simple keys (Nice! sounds like a perfect JSON column).&lt;br&gt;&lt;br&gt;
One identifies the app or platform where the content is available using a string with its name, and the other does the same for the market.&lt;/p&gt;

&lt;p&gt;Unless this is your first rodeo (and it’s not mine), at this point you’re probably seeing more red lights than a sinking submarine.&lt;br&gt;&lt;br&gt;
It’s pretty clear that, at some point—whether it’s a stakeholder, a PM, or a designer—someone’s going to ask for the ability to filter content by platform, by country, or by some combination of both.&lt;/p&gt;

&lt;p&gt;And if you love JSON columns as much as I do, you’ll know that filtering or querying them for anything slightly complex can quickly become yet another reason to move to the countryside and starve to death (because let’s be honest—you’d love to have a farm, but you wouldn’t know how to grow even a common cold).&lt;/p&gt;

&lt;p&gt;So I say goodbye to all that dopamine that was about to be released as a reward for a small and quick win, and I take off my cowboy hat to put on my thinking one.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧩 Modeling the entities
&lt;/h2&gt;

&lt;p&gt;To start doing things properly, I’ll need a table for &lt;strong&gt;apps&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Even if, for now, they only have a name, trust me—sooner or later they’ll start getting attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/app.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:content_availabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we’ll do the same for markets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/market.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Market&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:content_availabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know, it looks like overkill—but Rails migrations are free, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ ContentAvailability, the glue that holds it all together
&lt;/h2&gt;

&lt;p&gt;Now that we have our two entities, the next step is to pair them up inside a ContentAvailability.&lt;br&gt;
And this is where our dear old friend, the polymorphic association, comes back.&lt;/p&gt;

&lt;p&gt;It lets us define a belongs_to relationship with different types of models (thanks to Rails magic), which is exactly what we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/content_availability.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContentAvailability&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;polymorphic: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:market&lt;/span&gt;

  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:market_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="s2"&gt;"availability already exists for this app/market pair"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to close the loop, each content type (Movie, TvShow, Episode, etc.) includes the Contentable module, which already defines the polymorphic relationship:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/concerns/contentable.rb&lt;/span&gt;
&lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:content_availabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
&lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;through: :content_availabilities&lt;/span&gt;
&lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:markets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;through: :content_availabilities&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;One last important detail is the constraint that prevents a content item from having more than one availability for the same app/market pair.&lt;br&gt;
That’s reinforced at the database level too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;add_index&lt;/span&gt; &lt;span class="ss"&gt;:content_availabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:market_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="ss"&gt;unique: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'index_unique_content_availability'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🤠 Epilogue
&lt;/h2&gt;

&lt;p&gt;And just like that—with a few lines of code—we turned what could have been a JSON column nightmare into an elegant, normalized, and easily extensible solution.&lt;/p&gt;

&lt;p&gt;Could we have done it faster?&lt;br&gt;
Yes.&lt;br&gt;
Could we have done it worse?&lt;br&gt;
Absolutely.&lt;/p&gt;

&lt;p&gt;But honestly, there’s nothing more satisfying than watching a design that looked like overkill on Monday save you from a massive refactor on Friday.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧩 Full Code &amp;amp; Repository
&lt;/h3&gt;

&lt;p&gt;You can check out the full implementation, including models, migrations, and specs, on GitHub:  &lt;/p&gt;

&lt;h2&gt;
  
  
  👉 &lt;a href="https://github.com/develaper/stream-content-api" rel="noopener noreferrer"&gt;stream-content-api&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>api</category>
      <category>cowboy</category>
    </item>
    <item>
      <title>Polymorphic Relationships Matter (and My ADHD Agrees)</title>
      <dc:creator>develaper</dc:creator>
      <pubDate>Wed, 22 Oct 2025 08:41:09 +0000</pubDate>
      <link>https://forem.com/develaper/polymorphic-relationships-matter-and-my-adhd-agrees-236f</link>
      <guid>https://forem.com/develaper/polymorphic-relationships-matter-and-my-adhd-agrees-236f</guid>
      <description>&lt;p&gt;TL;DR: Building an API for movies, shows, and channels got messy fast. I ditched Single Table Inheritance for polymorphism, built a shared CatalogEntry index, and learned why “country” and “market” are not the same thing.&lt;/p&gt;

&lt;p&gt;A few days ago, I got an assignment that looked pretty harmless at first glance:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Build an API that exposes data for different types of audiovisual content.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Easy, right?&lt;br&gt;
&lt;strong&gt;Spoiler:&lt;/strong&gt; it wasn’t.&lt;/p&gt;

&lt;p&gt;The API needed to handle &lt;em&gt;movies&lt;/em&gt;, &lt;em&gt;TV shows&lt;/em&gt; (with their &lt;em&gt;seasons&lt;/em&gt; and &lt;em&gt;episodes&lt;/em&gt;), &lt;em&gt;channels&lt;/em&gt;, and &lt;em&gt;programs&lt;/em&gt; — each with its own structure, relationships, and availability rules.&lt;/p&gt;

&lt;p&gt;Nothing scary if you’ve been doing Rails for a while...&lt;br&gt;
until you realize there are &lt;strong&gt;six different content types&lt;/strong&gt;, all kinda similar but not really.&lt;br&gt;
And when something’s “kinda similar” in Rails, you know what’s coming:&lt;br&gt;
time to pick your poison — inheritance, polymorphism, or a desperate ActiveRecord hack.&lt;/p&gt;


&lt;h2&gt;
  
  
  🤠 The Cowboy Phase: Single Table Inheritance
&lt;/h2&gt;

&lt;p&gt;I’ll be honest — my first instinct was pure cowboy mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:contents&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:year&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:duration_in_seconds&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonb&lt;/span&gt; &lt;span class="ss"&gt;:other_properties&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom.&lt;br&gt;
One table, one &lt;code&gt;type&lt;/code&gt; column, one lazy JSON field for everything else.&lt;br&gt;
Problem solved.&lt;/p&gt;

&lt;p&gt;Until you start thinking about &lt;code&gt;TVShow&lt;/code&gt; ↔ &lt;code&gt;Season&lt;/code&gt; ↔ &lt;code&gt;Episode&lt;/code&gt; relationships, or how content availability depends on both &lt;em&gt;app&lt;/em&gt; and &lt;em&gt;market&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Suddenly your “clean” table turns into a minefield of &lt;code&gt;NULL&lt;/code&gt;s, &lt;code&gt;if type == 'Movie'&lt;/code&gt;, and scopes that make you want to close your laptop and move to the mountains.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧬 The Enlightenment: Separate Tables, Shared Brain
&lt;/h2&gt;

&lt;p&gt;So I took a step back.&lt;br&gt;
Each content type should have &lt;strong&gt;its own table&lt;/strong&gt; (&lt;code&gt;movies&lt;/code&gt;, &lt;code&gt;tv_shows&lt;/code&gt;, &lt;code&gt;seasons&lt;/code&gt;, etc.),&lt;br&gt;
but still share logic, validations, and relationships.&lt;/p&gt;

&lt;p&gt;That’s when Rails’ &lt;strong&gt;concerns&lt;/strong&gt; come to the rescue.&lt;/p&gt;

&lt;p&gt;Here’s my &lt;code&gt;Contentable&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/concerns/contentable.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Contentable&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:content_availabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;through: :content_availabilities&lt;/span&gt;
    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:markets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;through: :content_availabilities&lt;/span&gt;
    &lt;span class="n"&gt;has_one&lt;/span&gt; &lt;span class="ss"&gt;:catalog_entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;

    &lt;span class="n"&gt;after_create&lt;/span&gt; &lt;span class="ss"&gt;:create_catalog_entry&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;content_type&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;underscore&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_catalog_entry&lt;/span&gt;
    &lt;span class="no"&gt;CatalogEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uuid: &lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to polymorphic associations (&lt;code&gt;as: :content&lt;/code&gt;),&lt;br&gt;
any model that includes &lt;code&gt;Contentable&lt;/code&gt; becomes a &lt;strong&gt;first-class citizen&lt;/strong&gt; in the catalog.&lt;/p&gt;

&lt;p&gt;No inheritance spaghetti, no redundant code, just clean extensibility.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧠 Enter the Bridge: &lt;code&gt;CatalogEntry&lt;/code&gt; as the Universal Index
&lt;/h2&gt;

&lt;p&gt;To make the API truly dynamic (with a single endpoint like &lt;code&gt;/api/v1/content&lt;/code&gt;),&lt;br&gt;
I needed a shared registry where all content types could coexist —&lt;br&gt;
something like a &lt;strong&gt;universal index&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s where &lt;code&gt;CatalogEntry&lt;/code&gt; came in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CatalogEntry&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;polymorphic: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And its migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:catalog_entries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;id: :uuid&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;references&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;polymorphic: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, &lt;code&gt;CatalogEntry&lt;/code&gt; acts as a bridge to any content type — movie, TV show, program, whatever.&lt;/p&gt;

&lt;p&gt;Want to grab a content record dynamically?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;catalog_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CatalogEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;catalog_entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; Movie, TVShow, ChannelProgram...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want to filter by type?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;CatalogEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;content_type: &lt;/span&gt;&lt;span class="s1"&gt;'Movie'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, flexible, scalable — the way polymorphism was meant to be used.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤓 Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;STI looks elegant until it bites back.&lt;/strong&gt;&lt;br&gt;
If your models share &lt;em&gt;some&lt;/em&gt; fields but have different relationships, go polymorphic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concerns are criminally underrated.&lt;/strong&gt;&lt;br&gt;
They let you share real behavior, not just columns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A shared index (like &lt;code&gt;CatalogEntry&lt;/code&gt;) saves your API from chaos.&lt;/strong&gt;&lt;br&gt;
No more “six queries, one JSON” nonsense.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🧩 Full Code &amp;amp; Repository
&lt;/h2&gt;

&lt;p&gt;You can check out the full implementation, including models, migrations, and specs, on GitHub:  &lt;/p&gt;

&lt;h2&gt;
  
  
  👉 &lt;a href="https://github.com/develaper/stream-content-api" rel="noopener noreferrer"&gt;stream-content-api&lt;/a&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🚀 Next Episode: &lt;a href="https://dev.to/develaper/two-wrongs-doesnt-make-a-polymorphic-5503"&gt;Two wrongs doesn't make a Polymorphic.&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Next up, I’ll break down how I modeled content availability across apps and markets —&lt;br&gt;
and we'll see how a seemingly simple decision—saving availability in a JSON hash—became the perfect excuse to review polymorphic associations, constraints, and "unnecessary" migrations.&lt;br&gt;
You know, the usual stuff.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Spoiler: polymorphism made another cameo.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  💬 Let’s Talk
&lt;/h3&gt;

&lt;p&gt;Have you ever regretted choosing STI halfway through a project?&lt;br&gt;
Or maybe found an elegant hybrid between the two approaches?&lt;br&gt;
Drop your take in the comments — I love reading real-world war stories from the trenches.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>api</category>
      <category>cowboy</category>
    </item>
  </channel>
</rss>
