<?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: Roadie</title>
    <description>The latest articles on Forem by Roadie (@roadie).</description>
    <link>https://forem.com/roadie</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%2Forganization%2Fprofile_image%2F2080%2F2e68548e-a108-4e19-9024-ad0dcbe05319.png</url>
      <title>Forem: Roadie</title>
      <link>https://forem.com/roadie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/roadie"/>
    <language>en</language>
    <item>
      <title>Backstage Adoption: The Day 2 Problem</title>
      <dc:creator>Jian Reis</dc:creator>
      <pubDate>Thu, 06 Feb 2025 14:13:46 +0000</pubDate>
      <link>https://forem.com/roadie/backstage-adoption-the-day-2-problem-1gfo</link>
      <guid>https://forem.com/roadie/backstage-adoption-the-day-2-problem-1gfo</guid>
      <description>&lt;p&gt;Deploying Backstage is an &lt;a href="https://roadie.io/blog/what-to-think-about-when-youre-thinking-about-an-idp/" rel="noopener noreferrer"&gt;huge milestone&lt;/a&gt; for any engineering team. Whether you’ve set it up to improve service discoverability, streamline onboarding with templates, or enhance governance, the possibilities it unlocks are transformative. But here’s the hard truth: deploying Backstage is just the beginning. What comes next - &lt;a href="https://roadie.io/blog/roadie-solving-the-day-2-problem-with-backstage/" rel="noopener noreferrer"&gt;the Day 2 experience&lt;/a&gt; - is where the real work begins.&lt;/p&gt;

&lt;p&gt;Day 2 with Backstage is about moving beyond the initial setup and figuring out how to turn Backstage into a tool your developers use as part of their daily or weekly workflows, and in time, come to  rely on. Many teams start with specific use cases like &lt;a href="https://roadie.io/blog/3-strategies-for-a-complete-software-catalog/" rel="noopener noreferrer"&gt;service discoverability&lt;/a&gt; or &lt;a href="https://roadie.io/blog/how-to-define-engineering-standards/" rel="noopener noreferrer"&gt;software governance&lt;/a&gt;, but these don’t always translate neatly into widespread adoption. That’s because solving a single pain point doesn’t necessarily create long-term habits. To make Backstage stick, you need a strategic approach that aligns its features with your developers’ daily workflows. So, let’s explore why Backstage adoption often stalls, and how to set your organization up for sustained success.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build it and they &lt;del&gt;will&lt;/del&gt; might come
&lt;/h2&gt;

&lt;p&gt;It’s easy to assume that once Backstage is deployed, adoption will naturally follow. After all, it solves critical &lt;a href="https://roadie.io/blog/improving-and-measuring-developer-experience-with-backstage/" rel="noopener noreferrer"&gt;pain points&lt;/a&gt;: creating a unified service catalog, automating repetitive tasks with &lt;a href="https://roadie.io/blog/the-backstage-scaffolder-a-powerful-new-orchestration-tool/" rel="noopener noreferrer"&gt;templates&lt;/a&gt;, and introducing &lt;a href="https://roadie.io/blog/improving-and-measuring-developer-experience-with-backstage/#governance-standards-adherence-and-complexity-management" rel="noopener noreferrer"&gt;governance tools&lt;/a&gt; to improve software quality. But in practice, adoption requires much more than simply solving these problems. It requires building habits, demonstrating value, and integrating Backstage into the fabric of your engineering culture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy: treat Backstage like a product
&lt;/h2&gt;

&lt;p&gt;The key to overcoming adoption challenges is to treat Backstage like a product, not just a one-off project. This mindset is at the heart of successful adoption and ensures that Backstage evolves over time to meet the needs of its users. Treating it as a product means committing to continuous improvement, guided by user feedback and measurable outcomes. That does mean that if you’re a platform engineer, you may need to put on a product manager hat, thinking strategically about what your users need, prioritizing features, and continuously communicating the value of Backstage to developers and leadership alike. This means balancing the technical work with a clear focus on solving problems and delivering value internally, which may represent a big change.&lt;/p&gt;

&lt;p&gt;It needn’t be overwhelming though - start by engaging your most important internal users - your developers. Communicate with them to understand their workflows, pain points, and priorities. Backstage adoption is a collaborative effort, and the more involved your developers feel in shaping its direction, the more likely they are to embrace it. Use tangible metrics to track success, such as catalog completeness, daily active users, or ROI from key features like templates (engineering time saved for each template run, for instance). These metrics not only highlight areas for improvement but also help demonstrate value to leadership, ensuring sustained investment.&lt;/p&gt;

&lt;p&gt;The ‘treat it like a product’ approach to a developer portal such as Backstage is really well articulated by Adam Rogal, who leads Developer Productivity and Platform at DoorDash. In the podcast episode “&lt;a href="https://www.youtube.com/watch?v=een198m7gfg" rel="noopener noreferrer"&gt;Bootstrapping a Developer Portal&lt;/a&gt;,” Rogal shares how his team built their developer portal, DevConsole, with a clear focus on delivering immediate value to their engineering customers. By engaging engineers early and iterating based on their feedback, the platform team ensured that DevConsole directly addressed real pain points. This approach not only drove higher adoption but also built trust with their internal users.&lt;/p&gt;

&lt;p&gt;Rogal also underscores the importance of creating a community - not just of users but of contributors. By empowering teams to actively participate in shaping the portal, DoorDash fostered a culture of collaboration and continuous improvement. This communal effort allowed the portal to evolve alongside the organization’s needs, ensuring its long-term relevance and success.&lt;/p&gt;

&lt;p&gt;By adopting this product mindset and prioritizing user engagement, platform teams can transform Backstage into an indispensable tool that enhances productivity and aligns with organizational goals. The experience at DoorDash serves as a powerful example of how this approach can drive both adoption and satisfaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tactics for sustaining adoption
&lt;/h2&gt;

&lt;p&gt;While adopting a product mindset gives you the strategic foundation for long-term Backstage success, tactics offer some actionable focus areas on a day-to-day basis. Let’s explore some key tactics to help drive and sustain &lt;a href="https://roadie.io/tags/adoption/" rel="noopener noreferrer"&gt;adoption&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate catalog completeness
&lt;/h3&gt;

&lt;p&gt;A rich and accurate catalog is the foundation of Backstage, but manually maintaining it is time-consuming. The best approach is automation. By using integrations with GitHub or AWS, you can &lt;a href="https://roadie.io/blog/3-strategies-for-a-complete-software-catalog/" rel="noopener noreferrer"&gt;automatically populate the catalog&lt;/a&gt; with metadata from your repositories or clusters. In our experience, organizations with the highest levels of catalog completeness (90% or more) have all made extensive use of automations such as templates and scripts, and entity &lt;a href="https://roadie.io/docs/getting-started/autodiscovery/" rel="noopener noreferrer"&gt;autodiscovery&lt;/a&gt; and ingestion to reduce the manual effort involved. &lt;/p&gt;

&lt;h3&gt;
  
  
  Leverage templates for early wins
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://roadie.io/docs/scaffolder/writing-templates/" rel="noopener noreferrer"&gt;Templates&lt;/a&gt; are one of the easiest ways to showcase the value of Backstage. They can &lt;a href="https://roadie.io/blog/using-backstages-scaffolder-to-fill-up-your-catalog/" rel="noopener noreferrer"&gt;automate repetitive tasks&lt;/a&gt;, like setting up a new microservice or creating a CI/CD pipeline, saving developers significant time and ensuring software governance and best practice is baked in. The ROI here is often immediate and measurable - reducing a process from months to minutes is something both developers and leadership can get behind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure and communicate value
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://roadie.io/product/tech-insights/" rel="noopener noreferrer"&gt;Metrics&lt;/a&gt; are your best friend when it comes to adoption. Track data like catalog completeness, template usage (and time saved), and daily active users to understand what’s working and what needs improvement. Share these metrics with leadership to demonstrate the impact of Backstage and justify further investment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make adoption a cultural effort
&lt;/h3&gt;

&lt;p&gt;Adoption isn’t just a technical challenge - &lt;a href="https://roadie.io/blog/the-adoption-journey-initiatives-and-strategies/" rel="noopener noreferrer"&gt;it’s a cultural one&lt;/a&gt;. Evangelism plays a crucial role here. Host “lunch and learns,” demos, or office hours to show developers how Backstage can make their lives easier. Engage other engineering teams and create internal champions who can advocate for the platform within those teams. Adoption grows when developers see Backstage as part of their workflow, not an extra step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invest in custom plugins
&lt;/h3&gt;

&lt;p&gt;While Backstage’s open-source plugins are a great starting point, one of Backstage’s biggest selling points is its near limitless extensibility through &lt;a href="https://roadie.io/docs/custom-plugins/overview/" rel="noopener noreferrer"&gt;custom plugins&lt;/a&gt;. These &lt;a href="https://roadie.io/blog/live-custom-backstage-plugins-within-seconds/#deploying-custom-plugins-to-roadie" rel="noopener noreferrer"&gt;plugins&lt;/a&gt; can address your organization’s unique workflows and challenges, creating a toolset that developers can’t find elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan for Day 2 from Day 1
&lt;/h2&gt;

&lt;p&gt;The real value of Backstage lies not in its deployment but in its adoption. To unlock this value, you need to approach Backstage as a product - gathering feedback, iterating on features, and aligning its capabilities with your organization’s goals. Adoption doesn’t happen overnight, but with a strategic mindset and sustained effort, Backstage can become an indispensable tool for your engineering teams.&lt;/p&gt;

&lt;p&gt;So, as you set up Backstage, don’t just think about the first deployment. Think about what comes next - the Day 2 experience. Plan for it, invest in it, and you’ll set your organization up for long-term success.&lt;/p&gt;

</description>
      <category>backstage</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>internaldeveloperportal</category>
    </item>
    <item>
      <title>What to think about when you’re thinking about an IDP</title>
      <dc:creator>Jian Reis</dc:creator>
      <pubDate>Mon, 27 Jan 2025 16:17:51 +0000</pubDate>
      <link>https://forem.com/roadie/what-to-think-about-when-youre-thinking-about-an-idp-2i90</link>
      <guid>https://forem.com/roadie/what-to-think-about-when-youre-thinking-about-an-idp-2i90</guid>
      <description>&lt;p&gt;Thinking about implementing an Internal Developer Portal (IDP)? You're in good company - &lt;a href="https://www.gartner.com/en/information-technology/technology-adoption-roadmap" rel="noopener noreferrer"&gt;Gartner believes&lt;/a&gt; 80% of of platform engineering teams will use IDPs by 2026. There’s a growing sense that every forward-looking technology organization should “have an IDP,” but without a clear rationale as to the "why", this mindset risks building something that (at best) is broad and shallow but that fails to address the real issues slowing your teams down. If you focus on too many things at once, you could end up with a platform that may look impressive but doesn’t actually help people do their jobs.&lt;/p&gt;

&lt;p&gt;Instead, a successful IDP emerges when you carefully pinpoint and address the actual challenges developers face. Is discoverability a major and constant headache, with engineers spending hours trying to figure out who owns which service, or whether a certain piece of functionality already exists somewhere else? Are operations teams buried in tickets because developers need help every time they want a new environment or test database spun up? Do your devs waste time hopping between half a dozen interfaces to track deployments, check logs, and find documentation?&lt;/p&gt;

&lt;p&gt;These are the signals to tune into when you’re thinking about building an IDP. Aligning your IDP closely with well-understood problems ensures every feature is both purposeful and valued. Equally important, it &lt;a href="https://roadie.io/blog/the-adoption-journey-initiatives-and-strategies/" rel="noopener noreferrer"&gt;sets the stage for adoption&lt;/a&gt;. Developers and team leads won’t embrace a platform just because it’s there—they’ll embrace it if it truly solves their everyday pain points. By narrowing your focus to the challenges that matter, you can go deep on solutions that genuinely improve how your team works, rather than going wide and hoping something sticks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus on the Three Big Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Discoverability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Challenge:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you ever seen teams re-implement a piece of functionality simply because they didn’t know it already existed somewhere else? Or maybe someone spends half a day sifting through old wikis, outdated Confluence pages, and random Slack threads looking for an API endpoint. That’s poor discoverability in action. It’s not just about knowing what services exist, but also understanding who owns them, what their dependencies are, and where the latest &lt;a href="https://roadie.io/blog/adopting-backstage-documentation-and-support/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; or runbooks can be found. When this information is hard to find, it leads to frustration, wasted time, and sometimes unnecessary duplication of effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to Prioritize and Implement:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Service Catalog&lt;/strong&gt; that brings together all your services, their owners, docs, performance data, and runbooks in one easily searchable location. Using something like Backstage, you create a &lt;a href="https://roadie.io/blog/3-strategies-for-a-complete-software-catalog/" rel="noopener noreferrer"&gt;living directory&lt;/a&gt; of what your organization offers internally, so developers spend less time hunting and more time building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measuring Impact and ROI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Track how often teams ask questions like, “Do we have a service that does X?” or “Who’s responsible for Service Y?” If these inquiries drop significantly, you’ve hit a milestone. Similarly, if your onboarding times shrink—new hires who previously took weeks to understand the landscape now feel comfortable in days—that’s your IDP delivering tangible value.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Self-Service
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Challenge:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ever see a feature get stuck in limbo because the developer can’t get the right environment spun up? Or watch an ops team drown under a pile of infrastructure requests that never seem to end? Without self-service capabilities, your velocity takes a hit. Developers have to wait on someone else’s schedule to get a test database or a staging cluster. By the time the resource is ready, the developer may have lost context or moved on to something else. Multiply that by all the teams and projects in flight, and it’s a huge drag on efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to Prioritize and Implement:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Template-driven provisioning&lt;/strong&gt; and a*&lt;em&gt;automated pipelines&lt;/em&gt;* that let developers handle common requests themselves. &lt;a href="https://roadie.io/blog/the-backstage-scaffolder-a-powerful-new-orchestration-tool/" rel="noopener noreferrer"&gt;Pre-approved templates&lt;/a&gt; can ensure that every provisioned environment adheres to best practices and security standards, so you’re not just removing a bottleneck—you’re also improving consistency and reliability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measuring Impact and ROI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start by noting how long it currently takes to get a new environment—maybe it’s three days. After introducing templates, see if you’ve brought that down to a few hours or less. Fewer infrastructure-related tickets and faster environment turnarounds mean your teams can maintain their momentum, delivering features and fixes quicker than before (never mind the increase in &lt;a href="https://roadie.io/blog/improving-and-measuring-developer-experience-with-backstage/" rel="noopener noreferrer"&gt;developer productivity&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Developer Experience (DX)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Challenge:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Picture a developer’s daily routine: they log into one tool for CI/CD pipelines, another for metrics, another for logs, and a separate browser tab for documentation. This constant context-switching slows them down and increases cognitive load. Over time, this fragmented experience can lead to frustration and reduced morale. It’s not that your teams don’t have the tools—they might have too many, scattered across different interfaces with inconsistent user experiences and integration points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to Prioritize and Implement:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An IDP such as Backstage that has all the necessary plugins and integrations properly configured and working becomes a &lt;strong&gt;Single Pane of Glass&lt;/strong&gt; that consolidates these critical elements. By giving developers one place to view logs, metrics, deployments, code reviews, and documentation, you’re streamlining their workflow and cutting down on wasted mental effort. Having an IDP that pulls in data and functionality from multiple sources, presenting it in a coherent, intuitive manner is a surefire way to improve developer experience for your internal engineering teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measuring Impact and ROI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Survey your developers before and after implementation. Ask how easy it is to find information, how often they switch tools, and how smoothly they can move from coding to testing to deploying. You can also keep an eye on DORA metrics—if the team starts shipping more frequently or fixing issues faster, your integrated interface may be part of the reason why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tying It All Together
&lt;/h2&gt;

&lt;p&gt;These three challenges—discoverability, self-service, and DX—often feed into one another. Better discoverability saves teams from reinventing the wheel, reducing the complexity of what you need to maintain. Self-service capabilities speed up delivery, easing the workload on ops and freeing developers to move quickly. Improved DX lowers friction, streamlines daily workflows, and keeps developers happier and more productive. Together, these improvements create a virtuous cycle that helps your engineering organization move faster and build more resilient services.&lt;/p&gt;

&lt;p&gt;With that as a baseline, your IDP can really step up and take things a step further. For example, &lt;a href="https://roadie.io/blog/tech-insights-for-roadie-backstage/" rel="noopener noreferrer"&gt;Roadie’s Tech Insights&lt;/a&gt; can add another layer of value to your IDP by providing a data-driven, real-time view into the overall health and quality of your services. Rather than relying on anecdotal evidence or gut feelings, Tech Insights surfaces concrete metrics—like compliance scores, dependency health, and adherence to security best practices—within the same platform your developers already use. This makes it easier for engineering leaders to identify hot spots, measure improvement over time, and align investments with areas of greatest need.&lt;/p&gt;

&lt;p&gt;Ultimately, you don’t implement an IDP just to say you have one. You implement it because you’ve identified specific, costly problems and want to solve them. Start by pinpointing the real issues—where are you losing time, where are developers frustrated, where are processes too complex or opaque? Then map each problem to a targeted solution—like a service catalog, a set of ready-made provisioning templates, or an integrated interface—and track the results. By focusing on the areas that matter most, you ensure that your platform isn’t broad and shallow, but narrow and deep—truly making a difference where your teams need it most.&lt;/p&gt;

&lt;p&gt;Get the “why” and the “what” clear first. From there, your IDP will have real purpose, real value, and real staying power within your organization. It’ll be something people rely on, not just another system they’re forced to use.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>backstage</category>
      <category>internaldeveloperportal</category>
    </item>
    <item>
      <title>Understanding the Backstage System Model</title>
      <dc:creator>Sam Nixon</dc:creator>
      <pubDate>Thu, 12 Dec 2024 14:21:20 +0000</pubDate>
      <link>https://forem.com/roadie/understanding-the-backstage-system-model-4cm6</link>
      <guid>https://forem.com/roadie/understanding-the-backstage-system-model-4cm6</guid>
      <description>&lt;p&gt;The &lt;a href="https://backstage.io/" rel="noopener noreferrer"&gt;Backstage Internal Developer Portal&lt;/a&gt; is, at its heart, a software catalog. As a catalog, Backstage relies on a structured System Model to represent and organize individual items, in order to make it easier to find the information development teams need. When you are setting up or running Backstage you’ll often want to tweak this Model (or make wholesale changes to it) to make it fit your organization.&lt;/p&gt;

&lt;p&gt;In this blog we’ll explore the Backstage System Model and how you can extend it if you need to.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why do we need a system model?
&lt;/h1&gt;

&lt;p&gt;Catalogs require at least some structure. If you don’t have a common taxonomy for how to describe each element inside it then it lacks coherence, like a library with no labels on the shelves (or worse yet, contradictory labels). You could pour in all of your various repositories, components, gateways, resources and clusters into a catalog and it will closely resemble a giant blob of nothing.&lt;/p&gt;

&lt;p&gt;In a Catalog, information needs to be sorted to have value. Decisions need to be made about what gets included and what does not, and you need an idea of what goes where - how things are categorized now and how they should be categorized in the future.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Basics
&lt;/h1&gt;

&lt;p&gt;The Backstage data model is made up of nodes ("entities") and edges ("relationships").&lt;/p&gt;

&lt;h2&gt;
  
  
  Entities
&lt;/h2&gt;

&lt;p&gt;The Backstage data model is built around "entities." Entities are the core units within the Backstage catalog that represent various elements of your software ecosystem. &lt;/p&gt;

&lt;p&gt;Each entity is defined via metadata (name, description, labels etc), spec (custom properties), and relations (connections with other entities). In OSS Backstage this information is often piped into Backstage via YAML files that adhere to Backstage's entity specification. Sometimes entities can also come from "Providers" which provide the entity from some source of truth (i.e. &lt;a href="https://roadie.io/docs/integrations/okta/" rel="noopener noreferrer"&gt;Users and Group entities from Okta&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This model allows teams to maintain a structured, discoverable Catalog by distributing the load across every team who owns part of the Catalog. &lt;/p&gt;

&lt;p&gt;Friction Warning: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backstage advocates for distributed ownership (i.e. each team owns the information in the Catalog that represents the software that it owns) so it can be tricky to update your model and change it over time. For example, if you wanted to replace a Kind all of the various teams would need to update their catalog files. To get around this, a lot of self-hosted Backstage users have built API-based methods for mass updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Kinds
&lt;/h3&gt;

&lt;p&gt;Entities are grouped into Kinds. Kinds are like a aisle at a supermarket - everything within it is broadly cohesive and organised around similar principles.&lt;/p&gt;

&lt;p&gt;Kinds have a schema and they require a processor to correctly ingest them into the Catalog.&lt;/p&gt;

&lt;p&gt;You get some core Kinds out-of-the-box with Backstage, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt;: Defines larger business domains, organizing systems and components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System&lt;/strong&gt;: Higher-level abstraction representing a collection of components working together&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component&lt;/strong&gt;: Represents deployable units like services, websites, or libraries)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Friction Warning: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In OSS Backstage you can extend existing Kinds or write new Kinds to include whatever you’d like, but you need to build or modify a processor each time. That means writing code.&lt;/li&gt;
&lt;li&gt;You will also need consider the long-term impact of a new Kind. You’ll likely be supporting that Kind for a long time unless you want to deprecate it and force entities that use that Kind to fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Types
&lt;/h3&gt;

&lt;p&gt;Kinds have Types, allowing grouping within these larger buckets.&lt;/p&gt;

&lt;p&gt;Types can be defined on-the-fly. Nothing special is needed to make Types work, any team can create a new Type just by articulating it in their catalog-info.yaml file. &lt;/p&gt;

&lt;p&gt;Friction Warning: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This can lead to a Cambrian explosion of Types, so you may want to introduce some constraint there. Validation of Types is common.&lt;/li&gt;
&lt;li&gt;Annoying errors can creep into Types (i.e.&lt;code&gt;Website&lt;/code&gt; and &lt;code&gt;Wesbite&lt;/code&gt;) unless you’re validating them in some way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Relationships
&lt;/h2&gt;

&lt;p&gt;Relationships exist between entities to provide the connective tissue of the Backstage Catalog.&lt;/p&gt;

&lt;p&gt;Each Kind has a preset series of permissible relationships that are built when the processor runs for that Kind.&lt;/p&gt;

&lt;p&gt;For example, a simple Component might have some API relationships and dependencies defined:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```apiVersion: backstage.io/v1alpha1&lt;br&gt;
kind: Component&lt;br&gt;
metadata:&lt;br&gt;
  name: artist-web&lt;br&gt;
  description: The place to be, for great artists&lt;br&gt;
spec:&lt;br&gt;
  type: website&lt;br&gt;
  lifecycle: production&lt;br&gt;
  owner: artist-relations-team&lt;br&gt;
  system: artist-engagement-portal&lt;br&gt;
  dependsOn:&lt;br&gt;
    - resource:default/artists-db&lt;br&gt;
  dependencyOf:&lt;br&gt;
    - component:default/artist-web-lookup&lt;br&gt;
  providesApis:&lt;br&gt;
    - artist-api&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


## The Core System model

Out of the box, Backstage comes with a lot of built-in Kinds with attendant relationships so you can get started as quickly as possible. 

Some Kinds, like software templates and Locations are effectively atomic and compartmentalised away from other Kinds. The remainder are tied to how the Catalog is built and used to represented entities.

![Backstage System Model](//images.ctfassets.net/hcqpbvoqhwhm/2c89CI4rKHDNmWvCM2IHhM/37022568f4168c497956da8d9615511a/software-model-entities.drawio-3ce7f43dd236c3934209fde8f21a4d9e.svg)

They in effect represent "The Spotify Way" to model software. That’s not for everyone and won’t necessarily work perfectly for you. 

If that’s the case, you have two options:

- `Force it a little`: aka shoehorn your existing concepts into Spotify’s version. This works in a lot of cases, but is necessarily a compromise.
- `Re-model`: if that doesn’t do the trick, you need to get to work remodeling Backstage entity Kinds and types to fit your needs. Some can be done without code changes, but some need you to get your hands dirty.

# Going beyond the basics and extending the Backstage System Model

The Backstage framework is designed to be highly extensible, allowing you to modify or add new Kinds, Types, and Relationships based on the requirements of your organisation. 

That said, there are a few things you need to think about when extending the model:

### 1. No code extensibility

Backstage has flexibility baked on for a large degree of software definition. Using Types or built-in relationships handles for most situations when you want to model your software inside the Backstage System model. 80-90% of the time this will do the trick, but will often come with some degree of compromise. For example, let’s say you want to articulate `Value Streams` as a top level concept, but have to make do with `Value Streams` being a Type associated to the `Domain` Kind. It’s imperfect, but it’ll do in a pinch.

At Roadie, we evaluate and extend the System Model for our customers regularly. That works a lot of the time, but sometimes customers have niche requests that we don’t feel would benefit all our users. This is non-optimal. We want to customers the freedom to extend the model without talking to us or writing code. To achieve that we’re building a fully self-serve, no-code UI for dynamically generating Kinds and defining a system model that can be as arbitrary as you’d like: if you want a Kind called `purple-monkey-dishwasher` you should be able to have one. 

### 2. Extending the framework using code

Backstage is built around [providers](https://backstage.io/docs/features/software-catalog/external-integrations/) and [processors](https://backstage.io/docs/features/software-catalog/external-integrations/#custom-processors). Providers pull data in, processors manipulate and validate that data to build the Catalog entities and relationships. 

You can create wholly new providers to handle the ingestion of data from sources not currently handled by Backstage. The Backstage community has built  a lot of Providers over the years, but they may require tweaks to fit your specific use-case. For example, Roadie has rebuilt the GitHub provider to use webhook-based ingestion because the size of Catalog we habitually deal with break the GitHub rate limits

You can also modify processors for existing Kinds. For example to extend the list of allowed relationships between Kinds you need to tweak those processors.

You can also create wholly new processors to define new business logic or processes for manipulating and validating that data when you create a new Kind. Going back to the Value Stream example, now you can differentiate `Value Stream` from `Domain` and allow the Kinds to deviate usefully from one another. Maybe they each need different allowed relationships, or they’ll build their entities differently: the choice is yours.

### 3. Data

In the [out-of-the-box OSS Backstage model](https://backstage.io/docs/features/software-catalog/system-model/) the data for the system model comes from yaml files. This follows the GitOps model, where changes are made in git-tracked repositories and then ingested by other systems (in this case, the Backstage Catalog).

That means if you want to change or update your model you need to change all those files. That in turn means that opening PRs against every repos which contain a relevant yaml file. This is often a large undertaking, adding significant friction. That’s why most high-volume users of OSS Backstage have built API- and database-based mechanisms to do mass updates. Roadie has two: the Decorator UI and APIs to do a variety of different update patterns (idempotent updates to sync data from a source of truth into Backstage, or just pushing in whole entities via the Roadie Entities API).

# Levers to pull when extending the model

Below are some common methods for extending the Backstage data model:

### 1. **Custom Annotations**

Difficulty: Trivial

- **Why**: If you need to add metadata specific to your organization (like security labels, compliance levels, etc.), you can define custom annotations.
- **How**: Annotations are added as key-value pairs within the `metadata.annotations` field in your YAML definitions. These annotations can be used to enhance search functionality, create custom views, or provide additional context.
- **Example**: Adding `security-level: high` as an annotation for services that handle sensitive data allows you to quickly filter and prioritize compliance and monitoring for these services.

**References**:

- [Backstage Annotations Documentation](https://backstage.io/docs/features/software-catalog/well-known-annotations/#annotations): Documentation on creating custom annotations to extend metadata.



    ```yaml
    apiVersion: backstage.io/v1alpha1
    kind: Component
    metadata:
      name: fraud-detection-model
      description: "AI model for fraud detection"
      annotations:
        security-level: high

    ...
    ```



### 2. **Custom Types**

Difficulty: Easy

- **Why**: In cases where the existing entity types (Component, API, etc.) do not fit your specific resources, you can create custom entities.
- **How**: Define a new entity type in any valid catalog-info.yaml. This simple involves adding a new type to the `spec.type` in the YAML file.
- **Example**: Suppose you have machine learning models as a core resource in your project. You could define a new `model` type.

**References**:

- [Roadie Kinds and Types documentation](https://roadie.io/blog/kinds-and-types-in-backstage/) talks a lot about how to use Types without introducing problems



    ```yaml
    apiVersion: backstage.io/v1alpha1
    kind: Component
    metadata:
      name: fraud-detection-model
      description: "Machine learning model for fraud detection"
      annotations:
        security-level: high
    spec:
        type: model
      version: "1.0"
      trainingDataset: "transactions-v1"
      accuracy: "95%"
    ```



### 3. **Modifying Existing Kinds to Add Custom Relations**

Difficulty: Normal

- **Why**: Relationships between entities help you capture dependencies, ownership, and team structures within your catalog. If your use case involves additional relationship types, custom relations can improve representation.
- **How**: Modify the relevant processor for a given Kind to enable new types of relationships to be built for that kind. Then define relations within the `spec.relations` section of the YAML file.
- **Example**: Suppose you want to track models associated with data sources. You could create a custom relation `usesDataFrom`, linking ML models to the Resource entities that document data sources they rely on.

**References**:

- [Roadie Kinds and Types Documentation](https://roadie.io/blog/kinds-and-types-in-backstage/): Provides practical examples of defining and extending Kinds.



```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: fraud-detection-model
  description: "Machine learning model for fraud detection"
  annotations:
    security-level: high
spec:
    type: model
  version: "1.0"
  trainingDataset: "transactions-v1"
  accuracy: "95%"
  relations:
  - type: usesDataFrom
    targetRef: resource:exampleorg/some-data-source
    target:
      kind: resource
      namespace: exampleorg
      name: some-data-source
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. &lt;strong&gt;Creating Entirely New Custom Kinds&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Difficulty: Normal / Hard&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why&lt;/strong&gt;: When the System Model cannot adequately encapsulate how you build software or the relationships between various parts of your organisation, you will need to build a custom Kind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How&lt;/strong&gt;: Write a new processor for that Kind and define a custom schema for that Kind.  This ensures all entities adhere to required fields, valid types, and constraints, providing an additional layer of validation. Then add new catalog-info.yaml files for the new Kind to relevant resources, or modify existing catalog-info.yaml files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;: For the &lt;code&gt;MLModel&lt;/code&gt; entity, you could create a new Kind to represent that in your System model. Using that new Kind you could then model relationships  as &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;trainingDate&lt;/code&gt;, and &lt;code&gt;accuracy&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://backstage.io/docs/features/software-catalog/descriptor-format/" rel="noopener noreferrer"&gt;Backstage JSON Schema Documentation&lt;/a&gt;: Explains how to define and enforce custom schemas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Backstage is an extremely flexible framework for modelling software and once the building blocks and options are understood it’s simple enough to fully customise the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful links:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://backstage.io/docs/features/software-catalog/system-model" rel="noopener noreferrer"&gt;Backstage System Model&lt;/a&gt;: official docs and a good starter diagram for how entities in the Catalog interact.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://backstage.io/docs/features/software-catalog/life-of-an-entity" rel="noopener noreferrer"&gt;Backstage Entities&lt;/a&gt;: official docs on the lifecycle of entities&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://backstage.io/docs/features/software-catalog/well-known-relations" rel="noopener noreferrer"&gt;Backstage Relationships&lt;/a&gt;: official docs on how relationships work inside Backstage&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://roadie.io/blog/modelling-software-backstage/" rel="noopener noreferrer"&gt;Modelling software in Backstage&lt;/a&gt;: Roadie blog from 2021 about how to model software in Backstage using the core system model. This still represents a great primer on the out-of-the-box system model and how you could use it.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backstage</category>
      <category>platformengineering</category>
    </item>
    <item>
      <title>Why Hybrid is Best for IDPs</title>
      <dc:creator>David Tuite</dc:creator>
      <pubDate>Wed, 04 Dec 2024 12:01:18 +0000</pubDate>
      <link>https://forem.com/roadie/why-hybrid-is-best-for-idps-53k7</link>
      <guid>https://forem.com/roadie/why-hybrid-is-best-for-idps-53k7</guid>
      <description>&lt;p&gt;The Internal Developer Portal (IDP) market is only 5 or 6 years old, but we’ve seen a huge amount of growth in that time, and many different options emerging.&lt;/p&gt;

&lt;p&gt;According to Gartner, IDPs are reported as the most frequently piloted technology in their 2022 - 2024 Technology Adoption Roadmap Survey, and 75% of organizations with platform engineering teams will provide IDPs by 2026.&lt;/p&gt;

&lt;p&gt;With this growth, 3 main categories of Internal Developer Portal products have emerged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open-source&lt;/strong&gt; IDPs are exactly what you think. The code is open-source and you host and maintain it yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proprietary&lt;/strong&gt; IDPs are typically software as a service startups funded by venture capital. You purchase access to them just like you purchase access to PagerDuty or GitHub Enterprise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid&lt;/strong&gt; IDPs are a combination of open-source and proprietary. They’re built on an open-source foundation, but come with commercial support and proprietary features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I’m going to dive into each category one by one, and learn the pros and cons of each.&lt;/p&gt;

&lt;p&gt;I’ll also explain why I believe the Hybrid model brings the best of both worlds. The community, extensibility and lack of vendor lock-on of the open-source model, alongside the low cost, ease of use, and fast deployment of the proprietary model. &lt;/p&gt;

&lt;h2&gt;
  
  
  Open-source
&lt;/h2&gt;

&lt;p&gt;While technically not an IDP, and technically not the only open-source IDP, you can’t talk about open-source IDPs without the conversation focussing on Backstage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://roadie.io/backstage-spotify/" rel="noopener noreferrer"&gt;Backstage&lt;/a&gt; was open-sourced in early 2020 by Spotify. It was a rewrite of their internal IDP that they’d been using for years. It’s not an IDP because it’s actually a set of TypeScript libraries that developers can combine together to build an IDP, but it has still captured a huge amount of attention in the IDP space.&lt;/p&gt;

&lt;p&gt;When Backstage launched, it was often compared to &lt;a href="https://github.com/hygieia/hygieia" rel="noopener noreferrer"&gt;Hygieia&lt;/a&gt; from Capital One, which predates it by almost 4 years. Even after Backstage appeared, a third contender emerged with a strong start. Ride-hailing company Lyft threw their hat into the ring when they released &lt;a href="https://github.com/lyft/clutch" rel="noopener noreferrer"&gt;Clutch&lt;/a&gt; a few months after Backstage was open-sourced. Clutch describes itself as "an extensible platform for infrastructure management”.&lt;/p&gt;

&lt;p&gt;in 2024, Clutch popularity seems to have stalled, Hygieia is deprecated, and the reality is that Backstage dominates the open-source IDP market. Although not a perfect metric by any means, GitHub stars show the difference in popularity of each tool.&lt;br&gt;
&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/3ztAVoKhmjcidfD90xcIYq/20debb7c682bb4b7384a105e0f2645e1/idp1.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/3ztAVoKhmjcidfD90xcIYq/20debb7c682bb4b7384a105e0f2645e1/idp1.png" alt="idp1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of the open-source model for IDPs
&lt;/h3&gt;

&lt;p&gt;In many ways the open-source model is perfect for building an Internal Developer Portal. IDPs are a window into all of the tools that developers use. They’re the venerable “single pane of glass”.&lt;/p&gt;

&lt;p&gt;Developers use a lot of tools though, and the developer tool landscape is thousands of tools strong and growing. Even the &lt;a href="https://landscape.cncf.io/" rel="noopener noreferrer"&gt;Cloud Native Landscape&lt;/a&gt; alone has more than 1,000 tools in it’s ecosystem.&lt;/p&gt;

&lt;p&gt;How can a single IDP integrate with and plug-in to all these tools? Well, by creating an open-source community who will help with the effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huge community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is exactly what Backstage has accomplished. The Backstage community is huge and engaged. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backstage was the &lt;a href="https://www.cncf.io/reports/cncf-annual-report-2023/" rel="noopener noreferrer"&gt;top end-user contributed CNCF project of 2023&lt;/a&gt;, with more than 4,000 contributions from end user companies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits of the open-source model for IDPs
&lt;/h3&gt;

&lt;p&gt;In many ways the open-source model is perfect for building an Internal Developer Portal. IDPs are a window into all of the tools that developers use. They’re the venerable “single pane of glass”.&lt;/p&gt;

&lt;p&gt;Developers use a lot of tools though, and the developer tool landscape is thousands of tools strong and growing. Even the &lt;a href="https://landscape.cncf.io/" rel="noopener noreferrer"&gt;Cloud Native Landscape&lt;/a&gt; alone has more than 1,000 tools in it’s ecosystem.&lt;/p&gt;

&lt;p&gt;How can a single IDP integrate with and plug-in to all these tools? Well, by creating an open-source community who will help with the effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huge community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is exactly what Backstage has accomplished. The Backstage community is huge and engaged. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backstage was the &lt;a href="https://www.cncf.io/reports/cncf-annual-report-2023/" rel="noopener noreferrer"&gt;top end-user contributed CNCF project of 2023&lt;/a&gt;, with more than 4,000 contributions from end user companies.&lt;/li&gt;
&lt;li&gt;There are 17,000 people in the Backstage Discord channel, with dozens and dozens of questions being asked or answered every day.&lt;/li&gt;
&lt;li&gt;Backstage has a &lt;a href="https://backstage.io/community" rel="noopener noreferrer"&gt;deep partner ecosystem&lt;/a&gt;. Whether you’re a Fortune 50 enterprise or a Series C startup, there’s a partner who can help you deploy and use Backstage.&lt;/li&gt;
&lt;li&gt;Backstage has official support from leading DevTools companies. Companies like Snyk, PagerDuty, Dynatrace and even AWS have released officially supported plugins for Backstage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This vibrant community means that you can always get help with Backstage. The other benefit is the extensibility and the number of integrations available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huge number of integrations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The result of this large community is that Backstage is installed and adopted at thousands of organizations. These organizations use a diverse set of engineering tools, and many adopters have built and open-sourced Backstage plugins for these tools.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://backstage.io/plugins" rel="noopener noreferrer"&gt;Backstage plugins directory&lt;/a&gt; has more than 200 plugins available, so you can be fairly confident that there are plugins available for the tools you use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Highly extensible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Underlying all these plugins is a framework which is designed from the ground up to be maximally extensible. &lt;a href="https://backstage.io/docs/plugins/" rel="noopener noreferrer"&gt;The docs say&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Backstage is a single-page application composed of a set of plugins.&lt;/p&gt;

&lt;p&gt;Our goal for the plugin ecosystem is that the definition of a plugin is flexible enough to allow you to expose pretty much any kind of infrastructure or software development tool as a plugin in Backstage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Practically, this means you can customize and extend your Backstage install to your specific requirements. If you need to build your catalog in Gerrit rather than GitHub, then you can swap out the GitHub integrations and replace them. The same concept applies to most parts of Backstage.&lt;/p&gt;

&lt;p&gt;By coupling this technical extensibility with a permissive Apache 2.0 license and a &lt;a href="https://github.com/backstage/community/blob/main/GOVERNANCE.md" rel="noopener noreferrer"&gt;mature governance model&lt;/a&gt;, adopters can make wholesale changes to Backstage if they like.&lt;/p&gt;

&lt;p&gt;The result is that many organizations have customized Backstage heavily. Take a look at this screenshot of Sunrise, &lt;a href="https://platformengineering.org/talks-library/sunrise-zalandos-internal-developer-platform" rel="noopener noreferrer"&gt;the Backstage-based IDP that Zalando created&lt;/a&gt;, as an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/maWMqKoK0KLgvYfvTZn8F/cab20948d658be53033be67abe3f577f/idp2.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/maWMqKoK0KLgvYfvTZn8F/cab20948d658be53033be67abe3f577f/idp2.png" alt="idp2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This completely custom UI shows the progress of changes as they flow through CICD on their way to production. Very little of what you see here comes out of the box with Backstage. Instead, it’s wired together from the basic building blocks of Backstage plugins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lack of vendor lock-in&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because Backstage is open-sourced and backed by large end user companies and the CNCF, adopters can feel comfortable that it will be around for a long time, and will continue to meet their needs into the future.&lt;/p&gt;

&lt;p&gt;The IDP market is still very early. The majority of the players have been founded in the past 5 years. We will likely see some consolidation of this market as players get acquired and wound down. Any customers of these companies will be forced to migrate to a different solution.&lt;/p&gt;

&lt;p&gt;We’ve seen this play out in the CICD market already. There was a time when Travis CI was a dominant provider of continuous integration. In 2019, Travis CI was &lt;a href="https://news.ycombinator.com/item?id=18978251" rel="noopener noreferrer"&gt;acquired by private-equity firm Idera&lt;/a&gt;. One month later, &lt;a href="https://news.ycombinator.com/item?id=19218036" rel="noopener noreferrer"&gt;layoffs began&lt;/a&gt;. In early 2020, Travis CI &lt;a href="https://news.ycombinator.com/item?id=25338983" rel="noopener noreferrer"&gt;stopped providing free support for open-source products&lt;/a&gt;, and in late 2020 &lt;a href="https://news.ycombinator.com/item?id=24964601" rel="noopener noreferrer"&gt;the pricing changes began&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Companies can avoid this fate in the IDP market by building on an open-source project instead of a commercial vendor with it’s own data model and lock-in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawbacks of the open-source model of IDPs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;It’s expensive to build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Backstage is a larger undertaking than most people realize before they get into it. To quote Gartner in their &lt;a href="https://www.gartner.com/en/documents/4010078" rel="noopener noreferrer"&gt;Innovation Insight for Internal Developer Portals report&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gartner inquiries indicate that Backstage implementations may require substantial effort in standing up the service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to replicate something approaching the Sunrise portal mentioned above, companies should expect to allocate 2 to 5 engineers to the project for a number of years. &lt;/p&gt;

&lt;p&gt;Don’t believe me? Zalando said it themselves in &lt;a href="https://youtu.be/zowEfZoZycs?si=OYdNtCUZ9jee1ydu&amp;amp;t=1515" rel="noopener noreferrer"&gt;this presentation they did&lt;/a&gt; at the Autodesk Developer Productivity Summit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All that I’ve showed you was done through the contributions of many teams, but the core team was three engineers and a engineering manager.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That team has been working on Sunrise since 2020.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And that’s where my team is focussed on, actually like, the discovery part of our application landscape, and where we actually decided to, a few years ago in the beginning of the Backstage era in 2020 when Backstage was released, to actually onboard ourselves into Backstage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s do the math. Assume an engineer or engineering manager costs $250k per year, therefore the team of 4 costs $1 million per year. 4 years have passed since 2020, so they’ve spent $4 million in total on their core Backstage team. Add in the “contributions of many teams” and we could easily be talking about a $6 million outlay.&lt;/p&gt;

&lt;p&gt;Now you’re probably saying “yeah but we’re not as big as Zalando”, but trust me, even small companies will spend a half a million dollars pretty easily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It requires uncommon skills&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Backstage is typically owned and managed by platform teams or DevOps teams. These teams are probably skilled in YAML and kubernetes-native languages like Go.&lt;/p&gt;

&lt;p&gt;Backstage is written in TypeScript. The backend runs on NodeJS, and the frontend is written in React.&lt;/p&gt;

&lt;p&gt;Organizations who are considering self-hosted Backstage should consider whether or not they have the skills to develop in these languages. As mentioned above, Backstage is a set of libraries that users combine together to make a developer portal. That means you need to write TypeScript to make use of it. You need to understand frontend development, HTML and CSS to customize it. It may not make sene make sense to build up these skills in a Platform and Infrastructure organization because they will not be easily transferrable to other projects.&lt;/p&gt;

&lt;p&gt;The second set of uncommon skills that a Backstage deployment needs are project management and developer relations. Once it’s live, you need to evangelize it. You have to go out to the rest of the organization and teach them about it and how to use it. Are your platform developers really going to want to do this work?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s got a long time to value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The typical Backstage rollout has a few phases. It starts with an initial deployment, which typically takes  a few months to get a basic Backstage instance into production. Some parts of Backstage will work straight out of the box at this point. You’ll be able to use the scaffolder to create new software projects from predefined templates, for example.&lt;/p&gt;

&lt;p&gt;Other parts of Backstage require an adoption phase. The software catalog is usually one of them. In order to have a complete software catalog, teams must first put their software into it. &lt;/p&gt;

&lt;p&gt;In 2023, at BackstageCon North America, I &lt;a href="https://youtu.be/Ar9Tk1t6toQ?si=LfC7CgV77FowTUYr" rel="noopener noreferrer"&gt;spoke to the experiences of some Backstage adopters&lt;/a&gt; who had attempted this feat. I first explained that 60% of the Backstage adopters I had interviewed were attempting to populate their catalog by writing YAML files that contain metadata about their services (these are called &lt;code&gt;catalog-info.yaml&lt;/code&gt; files).&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/2djFTALB80KSSETrFon7ma/14cf1ced01166a13f0f17323626ab4ed/idp3.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/2djFTALB80KSSETrFon7ma/14cf1ced01166a13f0f17323626ab4ed/idp3.png" alt="idp3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then shared some quotes from adopters who had followed this path&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The catalog never really worked for us. We struggled with adoption and rate limiting. Half the info in the catalog was wrong when they first pushed adoption.&lt;br&gt;
&lt;strong&gt;2,000 engineers, 2 years experience&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The thing we struggled with the most was getting teams to create the YAML file. We spent 6 months actively encouraging teams to register their components with Backstage. After 1 year, only 30 or 40% of the active repos were there.&lt;br&gt;
&lt;strong&gt;120 engineers, 1 year experience&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without a lot of product development and project management, this catalog population process takes a long time. There are many Backstage adopters who are at less than 50% catalog completeness after two years with the tool. Even Spotify admit that &lt;a href="https://thenewstack.io/how-spotify-achieved-a-voluntary-99-internal-platform-adoption-rate/" rel="noopener noreferrer"&gt;Backstage adoption rate often stagnates at 10%&lt;/a&gt;. You may have dreams of your software catalog becoming a single pane of glass for all of the software development in your organization, but it simply cannot fulfill its purpose if the software is not in there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proprietary
&lt;/h2&gt;

&lt;p&gt;At the other end of the spectrum, there are a number of proprietary developer portals like Cortex and Port. &lt;/p&gt;

&lt;p&gt;The concept of proprietary developer portals is largely the same as Backstage. They have similar features and target a similar user in the engineering organization. The main differences are in their extensibility. Users can’t edit the code of a proprietary developer portal, and there won’t be a large community who are producing open-source plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of the proprietary model for IDPs
&lt;/h3&gt;

&lt;p&gt;While a proprietary developer portal may never be as extensible as Backstage, they certainly do have some benefits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quicker to get started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Proprietary IDPs will mostly just work out of the box. You don’t have to plan sprints and do research and write code to get some value. They come with admin interfaces where you can set some properties, connect up your tools, and starting using the product straight away. This is likely a better experience for platform engineers who don’t have the time or expertise to deploy and customize Backstage from scratch.&lt;/p&gt;

&lt;p&gt;When you do need to configure something, the documentation provided by a proprietary developer portal will likely be more comprehensive and up to date. They’re simply more motivated and able to produce high quality documentation than an open-source community. This makes setup faster and less confusing, and helps to prevent misconfigurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User-friendly interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Proprietary IDPs bring their own user interface (UI). They’re developed from the ground up with design systems in place to help to ensure consistency across integrations and pages. They also have designers and UI engineers working to implement design practices in a coherent way.&lt;/p&gt;

&lt;p&gt;While the Backstage core team have done a lot to put the tool in a good place, the reality is that Backstage’s default interface leaves a lot to be desired.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/2c5i5wKrKGkM4u3tJGTccS/ad6033f7fd1eafc4cc2414aa5e4695c3/idp4.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/2c5i5wKrKGkM4u3tJGTccS/ad6033f7fd1eafc4cc2414aa5e4695c3/idp4.png" alt="idp4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This reality is brought on by the combination of overly familiar Material UI libraries and community contributed code that can be hit-or-miss in terms of how it looks and feels.&lt;/p&gt;

&lt;p&gt;Backstages plugins can be contributed by anyone, so there’s no guarantee that they will look good, or look consistent. It’s very likely that different plugins will work completely differently even though they are installed in the same Backstage instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More guidance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Proprietary IDPs are more opinionated in how they should be used. When you work with a vendor you should get access to solutions engineering and customer success resources who can help to share lessons from other customers.&lt;/p&gt;

&lt;p&gt;Working with the open source can sometimes feel like being handed a box of car parts and asked to build a car. Sure you can figure it out eventually, but you’re going to make mistakes along the way, and it probably won’t be the most efficient path.&lt;/p&gt;

&lt;p&gt;Having a partner to work alongside and share lessons is worth a lot, and helps to cut the time to value for your IDP project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawbacks of the proprietary model for IDPs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vendor lock-in&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Proprietary IDPs have their own in-house APIs, their own data model, and their own way of doing things. Many of the options on the market are early stage startups. There’s a good chance some of them will disappear over the next few years. If you’re a customer, your IDP will go with them. &lt;/p&gt;

&lt;p&gt;When they do survive, you’ll likely be forced to choose between paying up for price hikes, or completing an expensive migration project to a different solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limited extensibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s not possible to change the code of a proprietary IDP. If there’s something about the way it works that doesn’t suit the needs of your company, your best hope is to contact support and hope that they have the desire and the roadmap space to change it.&lt;/p&gt;

&lt;p&gt;Most development organizations have some homegrown tools that have sprung up over the years internally. These tools only exist inside the company they were created, and no IDP vendor will have an out-of-the box integration for them. For these tools, you need to create your own plugins if you wish them to be part of your single pane of glass IDP. Some proprietary IDPs do support custom plugin development, but you’ll need to do so using only the concepts and libraries that the vendor provides, and the plugins you create will not be transferrable to any other solution.&lt;/p&gt;

&lt;p&gt;Proprietary IDPs have fewer integrations than the massive Backstage community. Even the most well funded proprietary vendor has 48 plugins listed on their integrations page, less than a quarter of the number that are available for Backstage. No matter how big this vendor grows their team, they won’t be able to compete with the 500+ companies who have contributed to Backstage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limited speed of execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The tech landscape is constantly shifting and evolving. Before serverless we had Kubernetes, before Kubernetes we had virtualization, before virtualization we had bare metal. In between and around these major platform shifts we had and continue to have a massive number of different types of serverless, containerization and virtualization options.&lt;/p&gt;

&lt;p&gt;A developer portal is supposed to wrap all of these shifts and technologies in order to give the best possible coverage over your internal engineering landscape. Proprietary IDPs may struggle to capture all of these technology shifts into the future, leading to gaps in the portal that platform teams put in front of their internal users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hybrid
&lt;/h2&gt;

&lt;p&gt;Last, but not least, we have the Hybrid model. This model takes an open-source foundation with hundreds of plugins and integrations available, and makes it available in an out-of-the-box format which is easy to get started with.&lt;/p&gt;

&lt;p&gt;This is what Roadie is. It’s the only standalone IDP based on Backstage, and it offers the best of both worlds - extensibility &lt;em&gt;and&lt;/em&gt; speed of execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huge number of integrations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The vast majority of plugins that have been created for Backstage can work on Roadie. &lt;a href="https://roadie.io/docs/integrations/" rel="noopener noreferrer"&gt;Our integrations library&lt;/a&gt; contains 70+ plugins and integrations today, and we’re adding more all the time, in line with customer requests. Once a new open-source plugin is created, it takes us about a day to make it available in Roadie.&lt;/p&gt;

&lt;p&gt;The only reason we wouldn’t add a Backstage plugin are that it’s of such low quality that it doesn’t work or provide any value.&lt;/p&gt;

&lt;p&gt;This means that Roadie customers can effectively choose from 200+ plugins and integrations that the Backstage community has created and use them on our platform. In fact, &lt;a href="https://github.com/RoadieHQ/roadie-backstage-plugins" rel="noopener noreferrer"&gt;we’ve created some of the most popular plugins ourselves&lt;/a&gt; and open-sourced them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Highly extensible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie strives to support as much customization of our IDP as possible. In addition to theming and branding, we support a custom data model, custom Backstage plugins, custom proxies, layouts, scaffolder actions and on and on. You name it, &lt;a href="https://roadie.io/blog/the-power-of-customization-making-backstage-work-for-you-with-roadie/" rel="noopener noreferrer"&gt;we can probably let you customize it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This means that you can customize Roadie to meet the specifics of your organization, improving adoption and facilitating ease of use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No vendor lock-in&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie is API compatible with Backstage. The same software metadata schema that works on Backstage will work on Roadie. Custom frontend plugins written for Backstage will also work on Roadie. We’re just supporting and extending the same APIs.&lt;/p&gt;

&lt;p&gt;This has two benefits for customers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They can migrate off self-hosted Backstage onto Roadie very easily. Numerous organizations have already done this. Simply connect us to your source code management tool and we’ll automatically ingest any software metadata files you’ve created.&lt;/li&gt;
&lt;li&gt;They can migrate back to self-hosted Backstage very easily. In fact, our Terms of Service dictate that we will give you your data back within 30 days of termination.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;User friendly interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie’s interface is vastly improved compared to the barebones Backstage experience. We’ve worked with our users to streamline how people use the product. This means better out of the box defaults and easier customization.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/1TIBiSInyg1DhJjCj5TvOm/797e7071e5948feec6242bdce60e63ca/idp5.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/1TIBiSInyg1DhJjCj5TvOm/797e7071e5948feec6242bdce60e63ca/idp5.png" alt="idp5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inexpensive&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In many cases, Roadie is 5 times cheaper than self-hosting Backstage. &lt;/p&gt;

&lt;p&gt;People tend not to realize this up front, but the most highly developed Backstage deployments in the world have cost millions of dollars. We talk to companies all the time who have built a team of 5+ engineers around Backstage and run those teams for 3+ years. The most expensive Backstage deployment we’ve seen cost $7.5 million dollars over 3 years.&lt;/p&gt;

&lt;p&gt;Roadie doesn’t require such an extensive team to be built around it. We take away all of the operations and deployment effort, and much of the customer support that has to happen. We also give you out-of-the box features like &lt;a href="https://roadie.io/product/tech-insights/" rel="noopener noreferrer"&gt;Scorecards&lt;/a&gt; and &lt;a href="https://roadie.io/product/access-control/" rel="noopener noreferrer"&gt;Role-Based-Access-Control&lt;/a&gt; that you would otherwise have to build yourself to realize the potential of your IDP.&lt;/p&gt;

&lt;p&gt;Lastly, we charge based on usage. You only pay for the engineers who write code which is tracked in the catalog. Everyone else gets to log in for free. This means you can ramp gradually throughout your org, rather than paying for the developement of the entire IDP up front.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requires few skills&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie doesn’t require any TypeScript skills. You just drag and drop plugins where you want them, and configure the product in our administration panels. It’s dead simple. This is perfect for platform teams who are trained on cloud-native technologies. We even have no-code plugins that you can use to display lists or charts inside the UI without writing any code. &lt;/p&gt;

&lt;p&gt;If you do need to make something completely custom and you don’t want to write a Backstge plugin yourself, we can sort you out with professional services to suit your needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short time to value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We’ve built a ton of features into Roadie to help shorten the time to value. For example, we help teams build out their catalog quickly and we see customers reaching a &lt;a href="https://roadie.io/blog/3-strategies-for-a-complete-software-catalog/" rel="noopener noreferrer"&gt;high level of catalog completeness within 4 months&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We also provide customer success and guidance to help organizations effect change with their IDP. We take the lessons we’ve learned over the years and use them to guide you to success in as short a time as possible.&lt;/p&gt;

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

&lt;p&gt;We’ve designed Roadie from the ground up with flexibility and easy adoption in mind. We believe engineering organizations should not waste their resources on undifferentiated heavy lifting projects like deploying an open-source IDP. At the same time, they shouldn’t be locked into a proprietary data model or limited in terms of what they can build or the integrations that are available.&lt;/p&gt;

&lt;p&gt;It turns out that you can have your cake and eat it too. An Internal Developer Portal can be both easy to use and supported by a massive and growing open-source community. This means that you can focus on unlocking productivity in your engineering organization while knowing you have a rock-solid and flexible foundation beneath.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’ve also written about the difference between IDPs and the IDP space in general over on &lt;a href="https://thenewstack.io/internal-developer-portals-is-open-source-enough/" rel="noopener noreferrer"&gt;The New Stack&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Image by &lt;a href="https://pixabay.com/users/mstork-18903484/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=5856452" rel="noopener noreferrer"&gt;MSTORK&lt;/a&gt; from &lt;a href="https://pixabay.com//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=5856452" rel="noopener noreferrer"&gt;Pixabay&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Migrating to Backstage’s New Backend: A Step-By-Step Guide</title>
      <dc:creator>Miklós Kiss</dc:creator>
      <pubDate>Tue, 05 Nov 2024 09:08:05 +0000</pubDate>
      <link>https://forem.com/roadie/migrating-to-backstages-new-backend-a-step-by-step-guide-71o</link>
      <guid>https://forem.com/roadie/migrating-to-backstages-new-backend-a-step-by-step-guide-71o</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Migrating to Backstage’s &lt;a href="https://backstage.io/docs/backend-system/" rel="noopener noreferrer"&gt;new backend system&lt;/a&gt; is more than just an upgrade - it’s a step toward a more scalable, efficient, and streamlined platform. The new backend introduces a modern architecture that simplifies plugin integration, reduces complexity, and improves overall performance. This is a significant shift from the monthly updates and incremental improvements you may be used to. By adopting this new architecture, you’ll gain better control over plugin dependencies, reduce overhead, and set the stage for easier future development.&lt;/p&gt;

&lt;p&gt;This migration is particularly exciting because it marks the culmination of a long journey towards a more modular and efficient backend. Originally introduced as an experimental feature, the new backend has matured into a fully supported system that greatly enhances how plugins interact with core services. It’s a big leap forward from the previous architecture, and while the transition may require some effort, the long-term benefits—such as improved scalability, clearer separation of concerns, and better dependency management—make it a worthwhile investment for teams looking to future-proof their Backstage setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this guide, we’ll focus on helping you migrate your existing Backstage backend with minimal disruption. We’ll walk through key steps, such as backing up your current system, creating a bridge for your existing plugins, and gradually integrating them into the new architecture. By the end of this article, you’ll have a clear understanding of how to approach the migration while maintaining your current functionality, and how to unlock the benefits of Backstage’s new backend. &lt;/p&gt;

&lt;p&gt;We’ll focus on performing a minimal migration that gets your main backend (&lt;code&gt;index.ts&lt;/code&gt; in &lt;code&gt;packages/backend&lt;/code&gt;) up and running in the new system. At this stage, we won't migrate all plugins, as that can be a more time-consuming process. Instead, we’ll leverage a temporary bridge function to convert existing plugins into the new system using a transitional environment, allowing you to start benefiting from the new architecture right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparation for Migration
&lt;/h2&gt;

&lt;p&gt;Before diving into the migration process, it's essential to ensure that your new system will function as expected post-migration. Proper preparation can significantly reduce the risk of issues arising during or after the transition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Establish a Testing Plan
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manual Testing&lt;/strong&gt;: Develop a comprehensive plan for manually testing your application with the new backend. This should include detailed scenarios that mimic real-world usage of your application. Identify critical paths and functionality that must work seamlessly in the new environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Tests&lt;/strong&gt;: In addition to manual testing, it's crucial to have a robust set of integration tests. These tests should cover all functionalities, particularly those related to any custom modifications you've implemented in your existing system. Ensure that your tests validate the expected behavior of all integrated components. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pay special attention to any custom features or modifications you’ve made. These areas are often the most prone to issues during migration, so thorough testing is essential. And of course it goes without saying, but make sure all your existing tests don’t fail! &lt;/p&gt;

&lt;p&gt;By laying the groundwork with a well-defined testing plan—comprising both manual and automated integration tests—you can increase your confidence in the migration process. This preparation will help ensure that your new backend system operates smoothly and consistently after the migration is complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migration Guide from Backstage
&lt;/h3&gt;

&lt;p&gt;Before proceeding with this migration guide, it's essential to familiarize yourself with the resources available from Backstage. The official &lt;a href="https://backstage.io/docs/backend-system/building-backends/migrating" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; on the Backstage website provides valuable insights and best practices for transitioning your backend system. While there may be some overlap with this guide, repetition can reinforce key concepts and ensure that you don't miss critical steps.&lt;/p&gt;

&lt;p&gt;Take the time to read through the Backstage migration guide before continuing with this document. Doing so will provide you with a solid framework and context that will aid in your migration efforts, ultimately leading to a smoother transition.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the Plan for Rollout?
&lt;/h3&gt;

&lt;p&gt;To ensure a seamless transition to the new backend, it’s crucial to carefully plan and prepare your rollout strategy. Here are some key considerations to guide your approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deployment Strategy&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New Image Creation&lt;/strong&gt;: Consider creating a new Docker image for the updated backend. This allows you to maintain clear versioning and ensures that your deployment environment matches your development and testing environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue-Green Deployment&lt;/strong&gt;: If feasible, implement a blue-green deployment strategy. This involves running two identical environments (blue and green) where one is live while the other is idle. You can switch traffic between them to minimize downtime and facilitate rollback if necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and Metrics&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set Up Monitoring Tools&lt;/strong&gt;: Implement monitoring solutions to track performance metrics, error rates, and resource usage. Tools like Prometheus, Grafana, or your existing APM solutions can provide valuable insights during and after the rollout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health Checks&lt;/strong&gt;: Ensure that health checks are in place to quickly identify any issues with the new backend as it goes live.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Migrating Your &lt;code&gt;index.ts&lt;/code&gt; to the New Backstage Backend System
&lt;/h3&gt;

&lt;p&gt;In this section, I’ll walk you through the process of migrating your Backstage backend to the new backend system. We’ll focus on updating your &lt;code&gt;index.ts&lt;/code&gt; file, which serves as the main entry point for your backend. This migration will allow you to start leveraging the streamlined architecture and dependency injection provided by the new system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Backup Your Existing &lt;code&gt;index.ts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Before starting any migration, it’s essential to create a backup of your current &lt;code&gt;index.ts&lt;/code&gt; file. This way, you can reference it or roll back if needed. Let’s save this backup as &lt;code&gt;index.backup.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, to avoid issues with type checking while you’re in the middle of the migration, add &lt;code&gt;@ts-nocheck&lt;/code&gt; at the top of your backup file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.backup.ts&lt;/span&gt;
&lt;span class="c1"&gt;// @ts-nocheck&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Set Up the New &lt;code&gt;index.ts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now, create a new &lt;code&gt;index.ts&lt;/code&gt; file, which will be the entry point for your new backend system. For this initial step, you can keep it as minimal as possible. The goal is to have a working backend skeleton, which we will build upon later.&lt;/p&gt;

&lt;p&gt;Here’s an example of a minimal setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createBackend&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/backend-defaults&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBackend&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your new backend doesn’t do much—it's just an empty shell. But it’s important to verify that the basic setup works before adding any of your legacy plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Next?
&lt;/h3&gt;

&lt;p&gt;In the next part of the migration, we’ll create a temporary legacy environment for your existing plugins and gradually integrate them into the new backend system. This approach allows for a smooth transition, letting you keep the old plugins running while starting to take advantage of the new architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a temporary plugin environment
&lt;/h2&gt;

&lt;p&gt;To ease the migration process, Backstage provides a handy bridge function, &lt;code&gt;makeLegacyPlugin&lt;/code&gt;, which helps create a temporary environment compatible with the old backend system. This allows your legacy plugins to continue working while you transition to the new backend architecture. The function ensures all required dependencies are injected into the plugin, simulating the old system’s behavior.&lt;/p&gt;

&lt;p&gt;Here's an example of using &lt;code&gt;makeLegacyPlugin&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;legacyPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeLegacyPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rootConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tokenManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eventsServiceRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;eventBroker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eventBrokerService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;httpAuth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pluginName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pluginMetadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;loggerToWinstonLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;cacheToPluginCacheManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&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;In this setup, your &lt;code&gt;makeLegacyPlugin&lt;/code&gt; function acts as a temporary replacement for the older &lt;code&gt;makeCreateEnv&lt;/code&gt; function. If &lt;code&gt;makeLegacyPlugin&lt;/code&gt; returns the same dependencies as &lt;code&gt;makeCreateEnv&lt;/code&gt;, you're set for a smooth transition.&lt;/p&gt;

&lt;p&gt;You can also provide type conversion functions within the second parameter to handle any type discrepancies between old and new service structures. For example, converting a logger or cache to the format expected by the new backend system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveats and Considerations
&lt;/h3&gt;

&lt;p&gt;One important note is that all of these dependencies will be instantiated for every plugin that uses the &lt;code&gt;legacyPlugin&lt;/code&gt; bridge. This can become problematic, particularly if any dependencies involve heavy operations (e.g., opening database connections). While this bridge is a useful short-term solution, it's highly recommended to fully migrate your backend plugins to the new system as soon as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the &lt;code&gt;legacyPlugin&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You can add your legacy plugins to the backend using &lt;code&gt;legacyPlugin&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;legacyPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./plugins/todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By focusing on migrating only your &lt;code&gt;index.ts&lt;/code&gt; file initially, you minimize disruption while keeping the migration manageable. This strategy allows you to stay on top of the process while taking advantage of the new backend architecture in stages, ensuring a smooth transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding new backend system plugins
&lt;/h2&gt;

&lt;p&gt;You can directly add your plugins that are already migrated to the new backend system into your &lt;code&gt;index.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;legacyPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./plugins/todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@roadiehq/foo-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// a backend plugin in the new system&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Overriding Core Services
&lt;/h2&gt;

&lt;p&gt;Backstage offers a robust architecture that includes a set of core services added to the application by default. When a plugin or service references a core service, it automatically receives the appropriate instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Implementations
&lt;/h3&gt;

&lt;p&gt;If you had custom implementations of certain services in your old system, you will need to override them in the new backend. This is crucial for maintaining functionality and ensuring that your custom logic remains intact. &lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended Approach
&lt;/h3&gt;

&lt;p&gt;To keep your Backstage directory structure organized and maintainable, I recommend creating a separate package for these service overrides. This approach promotes clarity and separation of concerns within your project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Package Naming Convention&lt;/strong&gt;: In the upstream OSS version of Backstage, this package is commonly referred to as &lt;code&gt;backend-defaults&lt;/code&gt;. Adopting this convention in your project will help standardize your codebase and make it easier for others to understand your structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create the &lt;code&gt;backend-defaults&lt;/code&gt; Package&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Set up a new package in your Backstage repository specifically for service overrides.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement Overrides&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Define the necessary overrides for the core services that require customization. Ensure that these overrides integrate seamlessly with the existing Backstage architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update Your Application&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Modify your application’s configuration to reference the new &lt;code&gt;backend-defaults&lt;/code&gt; package, ensuring that your custom implementations are used where needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The current core services that backstage provides are the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// backstage/backstage/packages/backend-defaults/src/entrypoints
auth
cache 
database
discovery
httpAuth
httpRouter
lifecycle
logger
permissions
rootConfig
rootHealth
rootHttpRouter
rootLifecycle
rootLogger
scheduler
urlReader
userInfo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can import the core services from the respective path like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;RootHttpRouterConfigureContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;rootHttpRouterServiceFactory&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/backend-defaults/rootHttpRouter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating Your &lt;code&gt;backend-defaults&lt;/code&gt; Package
&lt;/h3&gt;

&lt;p&gt;To effectively manage your service overrides in Backstage, you can create a &lt;code&gt;backend-defaults&lt;/code&gt; package using the Backstage CLI. This package will serve as a centralized location for your custom service implementations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step-by-Step Instructions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run the Backstage CLI Command&lt;/strong&gt;:&lt;br&gt;
Execute the following command to create a new package:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx backstage-cli new
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Select Package Type&lt;/strong&gt;:&lt;br&gt;
When prompted, choose the option for creating a Node library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node Library&lt;/strong&gt;: This will allow you to export shared functionality for backend plugins and modules.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set the Package ID&lt;/strong&gt;:&lt;br&gt;
Add the package ID as &lt;code&gt;backend-defaults&lt;/code&gt; when prompted. This step ensures that your package is correctly identified within your Backstage setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Package Creation&lt;/strong&gt;:&lt;br&gt;
Upon completion, this command will generate a new package in your &lt;code&gt;packages&lt;/code&gt; directory named &lt;code&gt;backend-defaults&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Organizing Your Overrides&lt;/strong&gt;:&lt;br&gt;
To keep your package structured and maintainable, I recommend creating a folder within the &lt;code&gt;backend-defaults&lt;/code&gt; package called &lt;code&gt;services&lt;/code&gt;. This folder will house your overrides or any new services you implement.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;packages/backend-defaults/services
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The following is an implementation of an override of the &lt;code&gt;coreServices.database&lt;/code&gt; service. This is a potential fix for cases where your &lt;code&gt;createEnv()&lt;/code&gt; and &lt;code&gt;pluginID&lt;/code&gt; strings did not match so in your old system the database name does not match the API path.&lt;/p&gt;

&lt;p&gt;In the new backend system it will always be the case that your plugin’s ID determines the name of its database and the path it will be attached to. This “fix” is needed because the current upstream Backstage doesn't provide a way to override the database name for these rare edge cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import &lt;span class="o"&gt;{&lt;/span&gt;
  coreServices,
  createServiceFactory,
&lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'@backstage/backend-plugin-api'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
import &lt;span class="o"&gt;{&lt;/span&gt; ConfigReader &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'@backstage/config'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
import &lt;span class="o"&gt;{&lt;/span&gt; DatabaseManager &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'@backstage/backend-defaults/database'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;const databaseServiceFactory &lt;span class="o"&gt;=&lt;/span&gt; createServiceFactory&lt;span class="o"&gt;({&lt;/span&gt;
  service: coreServices.database,
  deps: &lt;span class="o"&gt;{&lt;/span&gt;
    config: coreServices.rootConfig,
    lifecycle: coreServices.lifecycle,
    pluginMetadata: coreServices.pluginMetadata,
  &lt;span class="o"&gt;}&lt;/span&gt;,
  async createRootContext&lt;span class="o"&gt;({&lt;/span&gt; config &lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;config.getOptional&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'backend.database'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      ? DatabaseManager.fromConfig&lt;span class="o"&gt;(&lt;/span&gt;config&lt;span class="o"&gt;)&lt;/span&gt;
      : DatabaseManager.fromConfig&lt;span class="o"&gt;(&lt;/span&gt;
          new ConfigReader&lt;span class="o"&gt;({&lt;/span&gt;
            backend: &lt;span class="o"&gt;{&lt;/span&gt;
              database: &lt;span class="o"&gt;{&lt;/span&gt; client: &lt;span class="s1"&gt;'better-sqlite3'&lt;/span&gt;, connection: &lt;span class="s1"&gt;':memory:'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;,
            &lt;span class="o"&gt;}&lt;/span&gt;,
          &lt;span class="o"&gt;})&lt;/span&gt;,
        &lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  async factory&lt;span class="o"&gt;({&lt;/span&gt; pluginMetadata, lifecycle &lt;span class="o"&gt;}&lt;/span&gt;, databaseManager&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    const pluginId &lt;span class="o"&gt;=&lt;/span&gt; pluginMetadata.getId&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;let &lt;/span&gt;databaseName&lt;span class="p"&gt;;&lt;/span&gt;
    switch &lt;span class="o"&gt;(&lt;/span&gt;pluginId&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'foo-bar'&lt;/span&gt;:
        databaseName &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'foo_bar'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'tech-insights'&lt;/span&gt;:
        databaseName &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tech_insights'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      default:
        databaseName &lt;span class="o"&gt;=&lt;/span&gt; pluginId&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;databaseManager.forPlugin&lt;span class="o"&gt;(&lt;/span&gt;databaseName, &lt;span class="o"&gt;{&lt;/span&gt;
      pluginMetadata,
      lifecycle,
    &lt;span class="o"&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;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring the httpRouter service
&lt;/h2&gt;

&lt;p&gt;The backend system comes with its default configured httpRouterService. This is good for basic use cases. It contains multiple middleware and is responsible for the default configuration of the express router.&lt;/p&gt;

&lt;p&gt;If the default configuration is not enough, or you made customizations on your router in the old system you can provide your configurations like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;RootHttpRouterConfigureContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;rootHttpRouterServiceFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/backend-defaults/rootHttpRouter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;rootHttpRouterServiceFactory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootHttpRouterConfigureContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyDefaults&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// register your custom middlewares&lt;/span&gt;

            &lt;span class="nf"&gt;applyDefaults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// apply the default middlewares from the backstage core service&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;Backstage comes with a default request logger. You cannot turn it off and can not be overridden. If you want to replace it (or any of the default middleware) and use your own request logger you have only one choice: don’t apply the default middleware, and instead use each individual middleware as needed, and your custom implementations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;rootHttpRouterServiceFactory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootHttpRouterConfigureContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyDefaults&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;backstageMiddlewares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BackstageMiddlewareFactory&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="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// we leave out the backstageMiddlewares.logging() and use our own logger.&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;myCustomRequestLoggingHandler&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

            &lt;span class="c1"&gt;// add rest of the default middlewares&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backstageMiddlewares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;helmet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backstageMiddlewares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backstageMiddlewares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compression&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backstageMiddlewares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backstageMiddlewares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="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;h2&gt;
  
  
  Healthcheck
&lt;/h2&gt;

&lt;p&gt;If you want to override the default healthcheck you can easily do it by attaching a new endpoint for it.&lt;/p&gt;

&lt;p&gt;You can create a backend plugin for your healthcheck and then register it in your &lt;code&gt;index.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Use the &lt;code&gt;backstage-cli&lt;/code&gt; to create a new plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// run and &lt;span class="k"&gt;select   &lt;/span&gt;backend-plugin - A new backend plugin 
npx backstage-cli new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new backend plugin inside your project under the &lt;code&gt;plugins&lt;/code&gt;  folder. It will be called the ID that you provided in the prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// plugins/healthcheck-backend&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;healthCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBackendPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;pluginId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;healthcheck&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerInit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;rootHttpRouter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rootHttpRouter&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;rootHttpRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootLifecycle&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;rootHttpRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/healthcheck&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// packages/backend/src/index.ts&lt;/span&gt;
&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;healthcheck-backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the lifecycle hooks
&lt;/h2&gt;

&lt;p&gt;The new backed system provides a core service to hook into the different lifecycles of the process. There is a plugin scoped and a root scoped &lt;code&gt;lifecycle&lt;/code&gt; and &lt;code&gt;rootLifeCycle&lt;/code&gt; service respectively.&lt;/p&gt;

&lt;p&gt;In the old system you might have hooked into the service start promise. In the example below we call the function runOnStartup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// packages/backend/src/index.backup.ts&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServiceBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Startup finished`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;uptime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uptime&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nf"&gt;runOnStartup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;config&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The http server threw an unexpected error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;isNativeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`unknown error raised: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same functionality can be achieved with the lifecycle hooks in the new backend system. &lt;/p&gt;

&lt;p&gt;Create a module for your functionality. The example demonstrates how to add a lifecycle hook to the tech-insights plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;techInsightsModuleCalculateNew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBackendModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;pluginId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tech-insights&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;moduleId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calculate-new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerInit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rootConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;featureFlagStore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;featureFlagStoreServiceRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rootLifecycle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;featureFlagStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onStartup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nx"&gt;lifecycle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addStartupHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onStartup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Add the function to be run at startup&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Testing the New Backend
&lt;/h1&gt;

&lt;p&gt;Testing your migrated backend is a critical component of any upgrade process. Ensuring that your application functions as expected after migration can prevent a host of issues down the line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up a Development/Test Environment
&lt;/h3&gt;

&lt;p&gt;It is strongly recommend to establishing a dedicated dev/test environment. This allows you to deploy your new version and conduct tests with minimal traffic interference. Since we are re-architecting the entire Express application and modifying how plugins are integrated, validating that your plugins are accessible is absolutely paramount.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Health Check Tests
&lt;/h3&gt;

&lt;p&gt;A good starting point for testing is to implement basic health check tests using your existing end-to-end (e2e) or integration testing framework. These tests should verify that each plugin is mounted correctly to its expected path. Here’s an example of how you can do this with Playwright:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/healthcheck&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The healthcheck endpoint is configured&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/healthcheck`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reviewing End-to-End Test Coverage
&lt;/h3&gt;

&lt;p&gt;Before migrating, take a moment to review your end-to-end test coverage. Understanding your current testing landscape will help identify gaps, especially given the extensive changes involved in this migration. The more coverage you have over critical services, the better equipped you will be to catch issues early.&lt;/p&gt;

&lt;p&gt;Pay special attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Implementations&lt;/strong&gt;: Review any middleware, metrics, or logging mechanisms that may impact the migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Company-Specific Plugins&lt;/strong&gt;: Ensure that any frontend/backend plugins unique to your organization are covered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup Hooks&lt;/strong&gt;: Verify that these are functioning correctly post-migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legacy Functions&lt;/strong&gt;: Check the usage of custom legacy functions to avoid conflicts with plugin IDs and database names.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Manual Testing
&lt;/h3&gt;

&lt;p&gt;Finally, don't underestimate the importance of manual testing. Take the time to navigate through your application before completing the merge. The migration touches a broad surface area, making it challenging to cover every scenario with automated tests. If you've managed to create comprehensive automated tests—kudos to you! But a thorough manual check can help catch any edge cases that might otherwise be missed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;By following these testing practices—establishing a robust test environment, implementing health checks, reviewing your test coverage, and conducting manual tests—you can significantly reduce the risks associated with your backend migration. This proactive approach will help ensure a smooth transition and a reliable application post-upgrade.&lt;/p&gt;

&lt;h1&gt;
  
  
  Quirks Encountered in the New Backend System
&lt;/h1&gt;

&lt;p&gt;As we transitioned to the new backend system, we encountered several quirks that are important to highlight, particularly regarding database naming conventions and plugin path correlations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Name and Plugin Path Correlations
&lt;/h2&gt;

&lt;p&gt;In the updated architecture, database creation is directly tied to the plugin ID. While this design should streamline the process, it can lead to issues if there has been a lack of consistency in the previous backend, specifically within the &lt;code&gt;createEnv&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeCreateEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useHotMemoize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&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="nf"&gt;createEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo&lt;/span&gt;&lt;span class="dl"&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 the legacy system, the database name was simply derived from the parameter passed to the &lt;code&gt;createEnv&lt;/code&gt; function. The new backend would generate the databases using the plugin ID or the string you pass to the legacyBackend bridge function and it would use this same string as the API path. This is an issue if your parameter to the &lt;code&gt;createEnv&lt;/code&gt; function and the path you attached your plugin to were not the same.&lt;/p&gt;

&lt;p&gt;In the new system, the plugin ID takes precedence—it dictates both the database name and the API path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency is Crucial&lt;/strong&gt;: Ensure that you consistently use kebab-case for plugin identifiers across both the legacy and new systems to avoid unintended database creations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Official Override&lt;/strong&gt;: Currently, there is no configuration option to override this behavior, making it essential to align naming conventions proactively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Catching Errors in Plugin Configuration
&lt;/h2&gt;

&lt;p&gt;It's crucial to ensure that any misconfigurations or missing settings are promptly identified. One common issue is that if a plugin configuration is not available, the application may still start up without any visible indications of the problem. This can lead to missed error messages and difficult-to-diagnose issues later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Effective Monitoring Strategies
&lt;/h3&gt;

&lt;p&gt;To improve your visibility into potential errors during startup I recommend an approach to run your backend service with output filtering that focuses on error and warning messages. Here’s how you can do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start Your Application&lt;/strong&gt;: Launch your backend application as you normally would.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Grep for Real-Time Monitoring&lt;/strong&gt;: Pipe the output of your application to &lt;code&gt;grep&lt;/code&gt; to filter for key terms like "error" and "warn". This can be done in a Unix-like terminal using the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn start-backend | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"error|warn"&lt;/span&gt;

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


&lt;p&gt;This command allows you to see real-time log messages that could indicate issues with your plugin configuration.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By integrating these practices into your development workflow, you'll significantly enhance your ability to catch and respond to configuration errors on time.&lt;/p&gt;

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

&lt;p&gt;Understanding these quirks is vital for a smooth transition to the new backend system. By ensuring consistent naming conventions, you can mitigate potential issues related to database and plugin management. Catching errors and warnings early can save you a lot of time in the upgrade process. Stay vigilant to avoid complications that could arise from discrepancies in your configurations.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Image by &lt;a href="https://pixabay.com/users/generativestockai-18730327/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=8273245" rel="noopener noreferrer"&gt;GenerativeStockAI&lt;/a&gt; from &lt;a href="https://pixabay.com//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=8273245" rel="noopener noreferrer"&gt;Pixabay&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>internaldeveloperportal</category>
      <category>backstage</category>
      <category>migration</category>
    </item>
    <item>
      <title>Update the Backstage catalog instantly without touching any YAML</title>
      <dc:creator>Sam Blausten</dc:creator>
      <pubDate>Tue, 22 Oct 2024 08:59:18 +0000</pubDate>
      <link>https://forem.com/roadie/update-the-backstage-catalog-instantly-without-touching-any-yaml-4j25</link>
      <guid>https://forem.com/roadie/update-the-backstage-catalog-instantly-without-touching-any-yaml-4j25</guid>
      <description>&lt;p&gt;Updating an entity in the Backstage catalog generally means manually updating a YAML file in a repository somewhere and getting it merged to the main branch. &lt;/p&gt;

&lt;p&gt;For many, this manual process can feel like a series of hurdles—switching contexts, waiting for pull request reviews, and depending on other team members or systems. &lt;/p&gt;

&lt;p&gt;This friction increases significantly when you scale across hundreds or thousands of repositories. For example, changing a group name might require chasing after numerous teams to raise and merge PRs, turning what should be a simple update into a months-long process.&lt;/p&gt;

&lt;p&gt;Having to edit YAML makes keeping your catalog up-to-date a challenge, let alone enriching it with additional data or plugins like PagerDuty.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to add a PagerDuty plugin?
&lt;/h3&gt;

&lt;p&gt;Lets use the &lt;a href="https://www.pagerduty.com/" rel="noopener noreferrer"&gt;PagerDuty&lt;/a&gt; plugin as an example. Suppose you want to add PagerDuty monitoring to a Backstage entity representing a backend service. All you need to do is add an annotation to the YAML file with the relevant service ID.&lt;/p&gt;

&lt;p&gt;The quickest way to edit the YAML file might seem simple: open GitHub, click the pencil icon on the About card, and start editing.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/735n0qJci6DkFMQ9dqVQ6k/f7b1a9cbbae10426615091199f448504/Screenshot_2024-09-03_at_14.12.21.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/735n0qJci6DkFMQ9dqVQ6k/f7b1a9cbbae10426615091199f448504/Screenshot_2024-09-03_at_14.12.21.png" alt="About Card"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Manual Process in GitHub
&lt;/h4&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6cOOLbx3JM4UY0tL58bjp7/e2a087118b20dc6d37630ee9e9adccf1/Screenshot_2024-09-03_at_14.12.51.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6cOOLbx3JM4UY0tL58bjp7/e2a087118b20dc6d37630ee9e9adccf1/Screenshot_2024-09-03_at_14.12.51.png" alt="GitHub web editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once inside GitHub’s web editor, you can make the change, commit it to a new branch, and open a pull request. &lt;/p&gt;

&lt;p&gt;Sounds straightforward, right? But here’s where it gets tricky. To add the PagerDuty plugin, you need the correct service ID, which requires logging into PagerDuty, searching for the service that matches your entity, and then copying the ID from the browser’s address bar (since it’s not easily accessible in the UI).&lt;/p&gt;

&lt;p&gt;In GitHub’s case, we can edit this immediately, commit it to a new branch and open a pull request rather quickly all in the web editor. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6ZdSjaVPiHogqYHZOflezN/75e02a9824b64cf40a2f35b420695881/Screenshot_2024-09-03_at_14.16.12.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6ZdSjaVPiHogqYHZOflezN/75e02a9824b64cf40a2f35b420695881/Screenshot_2024-09-03_at_14.16.12.png" alt="PagerDuty annotation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But to find the correct Service ID we must first go to PagerDuty. Hopefully we can log in and access the available services, and then search for the correct one using the names in the Component YAML. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/7IYjPTjhIp9tdsLdoMAxO8/9c3f3b0797c88bf53209b059af3e3342/Screenshot_2024-09-03_at_14.19.48.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/7IYjPTjhIp9tdsLdoMAxO8/9c3f3b0797c88bf53209b059af3e3342/Screenshot_2024-09-03_at_14.19.48.png" alt="PagerDuty service search"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, after locating the ID (in the address bar) and adding it to the YAML file, you still need to commit the changes, submit a PR, and &lt;strong&gt;wait for it to be reviewed and merged&lt;/strong&gt;. Depending on the team’s review cadence, this could take days or longer.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/2Rs97gaqO8IDXtZ7sZ101b/8ad2f2dd91bbe2a6d5ca63d948f1803d/Screenshot_2024-09-03_at_14.23.47.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/2Rs97gaqO8IDXtZ7sZ101b/8ad2f2dd91bbe2a6d5ca63d948f1803d/Screenshot_2024-09-03_at_14.23.47.png" alt="Open PR"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Multiply this by hundreds of teams and thousands of repositories, and the process becomes incredibly slow and frustrating. Minor updates can take weeks or months to implement at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Way: Entity Updates with Roadie
&lt;/h2&gt;

&lt;p&gt;At Roadie we’ve built UI based tools that allow you to set things like PagerDuty annotations with a few clicks. Instead of seeing a missing annotation card you’ll be able to select from a list of PagerDuty services and update the entity immediately, all from the same page in the UI. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/4ZNEaRsdC1VrjU7FbECPyW/c680669b1593f99696cc7013bc7bcce1/Screenshot_2024-09-05_at_13.31.41.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/4ZNEaRsdC1VrjU7FbECPyW/c680669b1593f99696cc7013bc7bcce1/Screenshot_2024-09-05_at_13.31.41.png" alt="PagerDuty Annotation Card"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6a6dj4OCYYg4v5PfyibnOK/3f7ccad98074b58730a1dba66b449271/Screenshot_2024-09-05_at_13.31.50.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6a6dj4OCYYg4v5PfyibnOK/3f7ccad98074b58730a1dba66b449271/Screenshot_2024-09-05_at_13.31.50.png" alt="Select from existing services"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/22jEc6zi7JPchT3VVvpdri/2a4e593de5f3a675bcf163a1a9522d91/Screenshot_2024-09-05_at_11.52.23.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/22jEc6zi7JPchT3VVvpdri/2a4e593de5f3a675bcf163a1a9522d91/Screenshot_2024-09-05_at_11.52.23.png" alt="Working PagerDuty plugin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bulk updates via UI
&lt;/h3&gt;

&lt;p&gt;But what if you need to update multiple entities at once? Roadie also provides a bulk editor that allows you to make updates across many entities using a simple table format. This dramatically reduces the manual effort required to keep your catalog accurate and up-to-date.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/3P4MYwP1UEJXEmMaF3td5h/7eca2f377e30b4f043aecb83c0ef5f73/Screenshot_2024-09-05_at_14.06.30.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/3P4MYwP1UEJXEmMaF3td5h/7eca2f377e30b4f043aecb83c0ef5f73/Screenshot_2024-09-05_at_14.06.30.png" alt="Bulk annotation editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the Scenes: How It Works
&lt;/h2&gt;

&lt;p&gt;Our UI-based editors are powered by a backend feature we call Fragments. Fragments are partial entity data stored in a database table and merged into the existing entities in the catalog by a processor. These fragments can be updated through our UI or via API.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/DLOZ3BTheXhSHwRERRm9I/04d038ed679bdd91f0658fbdfef46642/Screenshot_2024-09-05_at_15.52.34.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/DLOZ3BTheXhSHwRERRm9I/04d038ed679bdd91f0658fbdfef46642/Screenshot_2024-09-05_at_15.52.34.png" alt="Overview of Backend flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Leverage external APIs to make things easier
&lt;/h3&gt;

&lt;p&gt;The custom UI we’ve seen earlier in this post also leverages external API’s to provide a dropdown of available options. &lt;/p&gt;

&lt;p&gt;In this example we’ve used PagerDuty’s API to fetch a list of services using the API token already added to Backstage to make your PagerDuty plugin work. This call is made via the &lt;a href="https://roadie.io/docs/details/create-proxy/" rel="noopener noreferrer"&gt;Backstage proxy&lt;/a&gt; which is set up to point to PagerDuty using your API Token on the backend. &lt;/p&gt;

&lt;h3&gt;
  
  
  Script bulk changes via API
&lt;/h3&gt;

&lt;p&gt;However, sometimes bulk editing of metadata in the catalog is best scripted, and in this scenario, Roadie users make use of our &lt;a href="https://roadie.io/docs/api/catalog/#operations-tag-Fragments" rel="noopener noreferrer"&gt;Fragments API&lt;/a&gt; to make changes to entities, such as changing the ownership of a group’s entities when the group gets absorbed into another group, or updating any relationship references to a user entity when that individual leaves and someone else takes their role. &lt;/p&gt;

&lt;p&gt;We’re not the only ones who’ve done something like this - companies like &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and others have built similar fragment functionality so that they can script updates to there catalog via API and free up platform teams to help keep the catalog data rich and accurate.&lt;/p&gt;

&lt;p&gt;Reach out to us on &lt;a href="https://discord.gg/jEMesd4ZdK" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or our website’s chat messenger if you want to hear more about this and other improvements Roadie has made to the Backstage experience.&lt;/p&gt;

</description>
      <category>backstage</category>
      <category>internaldeveloperportal</category>
      <category>yaml</category>
    </item>
    <item>
      <title>The Power of Customization: Making Backstage Work for You with Roadie</title>
      <dc:creator>David Tuite</dc:creator>
      <pubDate>Thu, 17 Oct 2024 08:48:37 +0000</pubDate>
      <link>https://forem.com/roadie/the-power-of-customization-making-backstage-work-for-you-with-roadie-17nh</link>
      <guid>https://forem.com/roadie/the-power-of-customization-making-backstage-work-for-you-with-roadie-17nh</guid>
      <description>&lt;p&gt;Backstage is the most flexible way to build an Internal Developer Portal that exists. The downside of this flexibility is that it can take a lot of effort to use. Roadie’s mission is to take the hassle out of Backstage, but we want to deliver on this goal without preventing our users from customizing Backstage to the fullest extent possible.&lt;/p&gt;

&lt;p&gt;This article explains many of the customization options available in Roadie today. From look-and-feel customization like theming, to deep data model flexibility, custom scaffolder actions, and completely custom plugins, Roadie has the flexibility you need. &lt;/p&gt;

&lt;h2&gt;
  
  
  Backstage customizability
&lt;/h2&gt;

&lt;p&gt;Backstage is not a Developer Portal. It’s actually a framework for building developer portals. You can think of it as being more like a Software Development Kit for building your own developer portal. Each company is expected to mix &amp;amp; match and extend the libraries that the Backstage community make available, so that they end up with a completely customized and unique IDP.&lt;/p&gt;

&lt;p&gt;This fact is evident from day one of using Backstage. To get started, you don’t download and run a Docker container like you might expect. Instead, you run a command line tool to scaffold your own Backstage instance. Once that’s done, you write your own code to customize it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;-&amp;gt; npx @backstage/create-app@latest
? Enter a name &lt;span class="k"&gt;for &lt;/span&gt;the app &lt;span class="o"&gt;[&lt;/span&gt;required]: acme-corp-idp

Creating the app....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example of this flexibility is evident in the plugin architecture. The Backstage community has created hundreds of open-source plugins that can be installed into a Backstage-based IDP in order to provide visibility into the many many tools that might be used. Backstage is designed in a way that makes these plugins easy to install and configure.&lt;/p&gt;

&lt;p&gt;From authentication hook points to source code management tool integrations to self-service scaffolder templates and UI components…. each and every part of Backstage can be ripped out and replaced and customized to your needs and desires. This is part of the philosophy of Backstage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadie customizability
&lt;/h2&gt;

&lt;p&gt;Despite being built on Backstage, Roadie is a complete, out-of-the-box, developer portal. We take away all of the work of building and maintaining your IDP, so that you can focus on getting value from the tool.&lt;/p&gt;

&lt;p&gt;We believe that most organizations shouldn’t need to build a team of 4 or 5 engineers around Backstage. You should want most of your engineering efforts focussed on initiatives that deliver direct customer value, rather than building internal tools from scratch.&lt;/p&gt;

&lt;p&gt;This is one of the reasons Roadie is delivered as Software as a Service. We want you to show up on day one and just start using it.&lt;/p&gt;

&lt;p&gt;At the same time, we also want to retain the philosophy of Backstage. You need to be able to customize Roadie just as much as you would customize Backstage. You should to be able to connect your own authentication providers, integrate your homegrown tools, and mix and match the plugins that make sense to you. For this reason, we strive to make Roadie as customizable and extensible as we possibly can.&lt;/p&gt;

&lt;p&gt;We believe that by combining Roadie’s ease of use with the flexibility and scope of the Backstage ecosystem, Roadie offers the best of both worlds, and is the best IDP on the market.&lt;/p&gt;

&lt;p&gt;This post explores many of the major ways you can customize Roadie to meet your needs. Here are your options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theming and UI
&lt;/h2&gt;

&lt;p&gt;Your IDP should look and feel familiar to your users, and it should focus their workflows in order to improve efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logos and branding
&lt;/h3&gt;

&lt;p&gt;Roadie let’s you replace the logos and branding across the site so that you can expose an IDP that matches your brand and themes.&lt;/p&gt;

&lt;p&gt;This image shows the dark-mode version of a theme for a fictional company called BeautyBox.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/670uquvHZffr8aHtvMxlam/1d71afd3c790d186c97a635a5f497828/customized-theme.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/670uquvHZffr8aHtvMxlam/1d71afd3c790d186c97a635a5f497828/customized-theme.png" alt="customized-theme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sidebar
&lt;/h3&gt;

&lt;p&gt;Roadie’s sidebar configuration lets you cut the navigation down to focus it on the most important use cases in your company. If you don’t need self-service automation capabilities in your IDP then you can easily remove them to streamline things. &lt;/p&gt;

&lt;p&gt;We also support reordering of sidebar sections, custom plugins in the sidebar, and deep links to important documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/FWWZqbbVXeKfK8UNmIvUA/3afa5a4c7fa91f68d44e58a99e18e9ed/sidebar.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/FWWZqbbVXeKfK8UNmIvUA/3afa5a4c7fa91f68d44e58a99e18e9ed/sidebar.png" alt="sidebar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Catalog customization
&lt;/h2&gt;

&lt;p&gt;The Roadie catalog offers control over how the catalog is collected in the first place, and how it is represented to end users who need to consume information in the catalog.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data model
&lt;/h3&gt;

&lt;p&gt;Many engineering organizations have custom terminologies they use to describe their particular software development life cycle. If your company uses Domain Driven Design, you might want top level concepts like “Value Streams” in your catalog. Other companies might want to support groups of people called “Tribes”. Probably every company will want a list of “Products”.&lt;/p&gt;

&lt;p&gt;Roadie supports a flexible and customizable data model that lets you rename existing Kinds and create your own types of entities. &lt;/p&gt;

&lt;p&gt;The screenshot below shows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backstage’s &lt;code&gt;Group&lt;/code&gt; kind renamed to “Teams”, &lt;/li&gt;
&lt;li&gt;A custom entity type called “Products” has been created,&lt;/li&gt;
&lt;li&gt;Unused kinds like Locations and Domains have been hidden from users.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/5w6J9Mvur1n2DaemlttJPx/713ad2db34650cc2ec8929c21ce5d8b1/custom-data-model.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/5w6J9Mvur1n2DaemlttJPx/713ad2db34650cc2ec8929c21ce5d8b1/custom-data-model.png" alt="custom-data-model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Table columns
&lt;/h3&gt;

&lt;p&gt;Different IDP users have different jobs, and that means they need different views. Roadie’s tables can be customized on a per user basis, so that everyone can streamline the UI to match the workflows they need.&lt;/p&gt;

&lt;p&gt;Users can turn columns on and off, resize columns and change the density of catalog tables. The best part is that these settings are persisted so you can easily dive back into your workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6rs6SDhqgOCatiZf6wB2v6/9d013d5ef10d7db637fbc6c7473c0bad/custom-columns.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6rs6SDhqgOCatiZf6wB2v6/9d013d5ef10d7db637fbc6c7473c0bad/custom-columns.png" alt="custom-columns"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The launch announcement covered &lt;a href="https://roadie.io/blog/august-2023-product-updates/#new-catalog-page-preview" rel="noopener noreferrer"&gt;the benefits of Roadie’s catalog UI&lt;/a&gt; in more depth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entity providers
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://backstage.io/docs/features/software-catalog/external-integrations/#custom-entity-providers" rel="noopener noreferrer"&gt;Custom entity providers&lt;/a&gt; are a way to provide entities into the catalog from external systems and existing data sources. They’re a critical integration point that helps ensure that your catalog will automatically stay up to date with external systems.&lt;/p&gt;

&lt;p&gt;Roadie makes it easy to use custom entity providers via the &lt;a href="https://github.com/RoadieHQ/roadie-agent" rel="noopener noreferrer"&gt;Roadie Agent&lt;/a&gt;. The agent is a library that you can dump entities into in order to have them appear in the catalog.&lt;/p&gt;

&lt;p&gt;Here’s a simple example where we use the Roadie Agent to dump an array of hardcoded entity metadata.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RoadieAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createRoadieAgentEntityProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@roadiehq/roadie-agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakePayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Standard Backstage entity metadata omitted for brevity&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myEntityProviderHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakePayload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;RoadieAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEntityProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;createRoadieAgentEntityProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testprovider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myEntityProviderHandler&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto-discovery settings
&lt;/h3&gt;

&lt;p&gt;Any software metadata in YAML files should be auto-discovered by Roadie so that engineers don’t need to remember to register it via the UI.&lt;/p&gt;

&lt;p&gt;Roadie supports &lt;a href="https://roadie.io/docs/integrations/github-discovery/" rel="noopener noreferrer"&gt;custom auto-discovery settings&lt;/a&gt;, including glob matching.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/7bn1PECDmPvL8k3LG9oUQ6/18b5cf96c291e4ded2d5b8d12b6e0dff/custom-audodiscovery.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/7bn1PECDmPvL8k3LG9oUQ6/18b5cf96c291e4ded2d5b8d12b6e0dff/custom-audodiscovery.png" alt="custom-audodiscovery"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Group and User ingestion
&lt;/h3&gt;

&lt;p&gt;Roadie needs an organization chart in order to ensure that ownership of software can correctly be assigned to teams (aka. Groups), and to ensure that &lt;a href="https://roadie.io/blog/rollups-tech-insights/" rel="noopener noreferrer"&gt;scorecard data is correctly rolled up&lt;/a&gt; through the org chart.&lt;/p&gt;

&lt;p&gt;The best way to make sure the org chart is up-to-date and correct is to automatically sync it from a HR tool. To facilitate this, Roadie supports built in integrations for &lt;a href="https://roadie.io/docs/integrations/ms-graph-org-provider/#step-3-configure-your-microsoft-graph-org-ingestion-provider" rel="noopener noreferrer"&gt;Microsoft Graph&lt;/a&gt; (also known as Microsoft Entry ID, or Azure Active Directory) and &lt;a href="https://roadie.io/docs/integrations/okta/" rel="noopener noreferrer"&gt;Okta&lt;/a&gt;. Other solutions can easily push Users and Groups into the catalog &lt;a href="https://roadie.io/docs/details/entity-push-api/" rel="noopener noreferrer"&gt;via our API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugins
&lt;/h2&gt;

&lt;p&gt;The real power of the Backstage ecosystem comes from its plugins. Roadie supports more than &lt;a href="https://roadie.io/docs/integrations/" rel="noopener noreferrer"&gt;70 Backstage plugins&lt;/a&gt; out of the box at the time of writing (September 2024). It typically takes us about a day to integrate a new open source plugin once it’s created by the community. &lt;/p&gt;

&lt;h3&gt;
  
  
  Plugin configuration
&lt;/h3&gt;

&lt;p&gt;Most plugins have configuration options that Roadie admins need to be able to configure to suit their particular setup. This is easily done via admin panels we have built into the product.&lt;/p&gt;

&lt;p&gt;Take the &lt;a href="https://roadie.io/backstage/plugins/argo-cd/" rel="noopener noreferrer"&gt;Argo CD plugin&lt;/a&gt; for example. Roadie supports two completely separate ways of integrating with Argo CD:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The app locator method allows you to dynamically search and identify Argo CD registered applications from multiple Argo CD instances.&lt;/li&gt;
&lt;li&gt;The proxy method is used to construct links to individual Argo CD applications. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can choose the best one for your needs, and even configure minute details like Namespace and Resource allowlists and blocklists.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6yC7BTsQPJxsXOC6nNOokP/b251bb4b30d5f9d63d8d8ce54c4432d7/argo-cd-plugin-config.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6yC7BTsQPJxsXOC6nNOokP/b251bb4b30d5f9d63d8d8ce54c4432d7/argo-cd-plugin-config.png" alt="argo-cd-plugin-config"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The APIs of your Argo CD instances are probably not exposed on the public internet, but it’s still easy for Roadie to access them securely. We provide an &lt;a href="https://roadie.io/docs/integrations/broker/" rel="noopener noreferrer"&gt;open-source broker&lt;/a&gt; that runs in your infrastructure and makes an outbound connection to Roadie.&lt;/p&gt;

&lt;p&gt;For other plugins, like the Kubernetes plugin and EKS, we support AWS Role Sharing as the preferred configuration method. Whatever your connectivity needs, we probably have a solution for it.&lt;/p&gt;

&lt;p&gt;Of course Roadie’s responsibilities don’t begin and end with plugin configuration. We’re also: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vetting plugins for quality issues as we integrate them,&lt;/li&gt;
&lt;li&gt;Scanning plugins for licensing issues (which your legal team will be happy about), &lt;/li&gt;
&lt;li&gt;Documenting plugins so people know how to use them, &lt;/li&gt;
&lt;li&gt;Updating plugins as their authors release new features.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Frontend plugins
&lt;/h3&gt;

&lt;p&gt;If there isn’t a community created plugin that suits your needs, or you need a plugin for a homegrown tool that only exists in your company, Roadie can support that too. &lt;/p&gt;

&lt;p&gt;Users can write native Backstage plugins that run on Roadie and securely connect back to your private internal APIs via the broker.&lt;/p&gt;

&lt;p&gt;The plugin development workflow supports the ability to locally host custom plugins and run them right on Roadie during development. This basically means that you can make a change to your plugin code in your IDE, and simply refresh Roadie to see your changes live. &lt;/p&gt;

&lt;p&gt;We also support running production and development versions of the same custom plugin in Roadie at the same time, so you can iterate on your plugins without disrupting your users.&lt;/p&gt;

&lt;p&gt;Productionizing a plugin is as simple as running the Roadie CLI against it to package it up and push it to Roadie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;roadie plugin:build &lt;span class="nt"&gt;--location&lt;/span&gt; /my-custom-plugin-folder/ &lt;span class="nt"&gt;--host&lt;/span&gt; https://static-assets.roadie.so/&amp;lt;my-tenant&amp;gt;/myCustomPlugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full range of Backstage frontend APIs are available to your custom plugins. You can query the catalog, check Role Based Access Control permissions, and even push analytics to see how people are using your plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proxies
&lt;/h3&gt;

&lt;p&gt;Plugins frequently make use of proxies to upgrade requests with token authentication before forwarding them on to a backend service to retrieve data. Roadie users can create their own proxies inside the application. These proxies can be used to connect to third-party SaaS APIs like PagerDuty, or private internal APIs.&lt;/p&gt;

&lt;p&gt;Here’s a user created proxy for SonarCloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/uNqwnrwzkXX4o1K7OoJLX/1b835014c9d6a63a6d20d6c16a649215/sonarcloud-proxy-config.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/uNqwnrwzkXX4o1K7OoJLX/1b835014c9d6a63a6d20d6c16a649215/sonarcloud-proxy-config.png" alt="sonarcloud-proxy-config"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Entity Pages
&lt;/h2&gt;

&lt;p&gt;Entity pages are the pages in the catalog which pull together information on a particular piece of software, a team, or an infrastructure resource. &lt;/p&gt;

&lt;h3&gt;
  
  
  UI layouts
&lt;/h3&gt;

&lt;p&gt;Adding plugins to Roadie interfaces is a simple matter of picking them off a list. This process is the same for both community created plugins (including plugins created by Roadie, plugins created by Spotify, and official plugins from companies like PagerDuty and Snyk) and for custom plugins you create yourself.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/5PVqyqFluRAkjxiEN4BEJz/b4c4f2d272318fb363f7184430e95647/add-card.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/5PVqyqFluRAkjxiEN4BEJz/b4c4f2d272318fb363f7184430e95647/add-card.png" alt="add-card"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve chosen your plugin, simply drag-and-drop it on the page, and resize it to suit your needs.&lt;/p&gt;

&lt;p&gt;Different types of entities need different layouts. The &lt;a href="https://roadie.io/backstage/plugins/lighthouse/" rel="noopener noreferrer"&gt;Backstage Lighthouse plugin&lt;/a&gt; makes sense for a website, but not for a Kubernetes cluster. Roadie organizes the plugins in this way so that it’s easy to create custom views without too much setup. Our built in role based access control ensures that admins have the power to set up layouts, without overloading regular users with too many options. Team pages are customizable in the same way, they just typically have different cards and widgets. &lt;/p&gt;

&lt;h3&gt;
  
  
  Props on plugins
&lt;/h3&gt;

&lt;p&gt;Sometimes individual cards will have their own editable properties. Roadie supports that too. For example, this card displays a list of recent GitHub Actions CI runs and their status. But how many recent CI runs should be displayed, and what layout should be used? Well, you can set that with the editable props.&lt;/p&gt;

&lt;p&gt;This screenshot shows the edit mode of the Dynatrace Backstage plugin.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/7tkVE8VIsIytv7dTGiMRd1/9e55dc3736d6628645a7e3ef62665f3f/dynatrace-plugin-props.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/7tkVE8VIsIytv7dTGiMRd1/9e55dc3736d6628645a7e3ef62665f3f/dynatrace-plugin-props.png" alt="dynatrace-plugin-props"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Metadata cards
&lt;/h3&gt;

&lt;p&gt;We’re aware that many platform teams wish to display custom information in Roadie, but don’t necessarily have the TypeScript skills on hand to create custom plugins from scratch. To make this easier, Roadie includes no-code cards that can display information from custom metadata on the Entity.&lt;/p&gt;

&lt;p&gt;Imagine we have a Backstage entity with some custom properties in its metadata.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backstage.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Component&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;group:roadiehq/engineering&lt;/span&gt;
  &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;engineeringManager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user:dtuite&lt;/span&gt;
    &lt;span class="na"&gt;productManager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user:samnixon87&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How would we display this in the catalog? On Roadie, it’s as simple as adding the &lt;code&gt;EntityMetadataCard&lt;/code&gt; and configuring it in a couple of clicks. The card even auto-links the Engineering Manager and Product Manager to their User pages in the catalog.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/7Cl1juhJ6gq3Andvtd43ZG/80d2c55d6afe3d9c76858ce06fc5b0b1/custom-metadata-card.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/7Cl1juhJ6gq3Andvtd43ZG/80d2c55d6afe3d9c76858ce06fc5b0b1/custom-metadata-card.png" alt="custom-metadata-card"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Home page
&lt;/h3&gt;

&lt;p&gt;Of course it’s not just pages in the software catalog that can be customized. Roadie users can also customize the &lt;a href="https://roadie.io/docs/integrations/home-page/" rel="noopener noreferrer"&gt;Home page plugin&lt;/a&gt; to display key information that’s relevant to them. This is the place to display your open pull requests, community news, your calendar, and other useful info that’s tailored to the logged in user’s experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/w3ZLsGikg07UecgfSjWxY/a5d7627225e1b54b7f0f30d5912689ac/Screenshot_2024-06-20_at_15.49.28.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/w3ZLsGikg07UecgfSjWxY/a5d7627225e1b54b7f0f30d5912689ac/Screenshot_2024-06-20_at_15.49.28.png" alt="Roadie-instance-homepage-example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffolder
&lt;/h2&gt;

&lt;p&gt;Roadie scaffolder supports the full range of customization that you would expect from the Backstage scaffolder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Actions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://roadie.io/docs/scaffolder/self-hosted-scaffolder-actions/#writing-a-template-using-custom-actions" rel="noopener noreferrer"&gt;Custom scaffolder actions&lt;/a&gt; are a way to execute homegrown CLI’s or arbitrary code as part of scaffolder templates. These are executed within your own infrastructure, which provides an added benefit in that it makes it easy to send internal network requests.&lt;/p&gt;

&lt;p&gt;Users can use the &lt;a href="https://github.com/RoadieHQ/roadie-agent?tab=readme-ov-file#custom-scaffolder-action" rel="noopener noreferrer"&gt;Roadie Agent&lt;/a&gt; (the same library used in the Custom Entity Providers section above) to register custom scaffolder actions with Roadie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;RoadieAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addScaffolderAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;createRoadieAgentScaffolderAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-action&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The name of the action as defined in Roadie&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspacePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/test.txt`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new file with new contents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt; 
          &lt;span class="c1"&gt;// Writing a new file into the shared workspace&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Local logging on the Roadie Agent process&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// Additional other actions that is wanted to be taken. This time looping for 5 seconds&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
          &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`hello world`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sending a log message to be displayed to the end user&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; 
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Add a second custom scaffolder action&lt;/span&gt;
  &lt;span class="c1"&gt;// .addScaffolderAction(...) &lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These can then be used in your templates like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# This step executes on Roadie&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fetchTemplate&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fetch file&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fetch:template&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./skeleton&lt;/span&gt;

  &lt;span class="c1"&gt;# This sends the workspace context to a self-hosted scaffolder runner&lt;/span&gt;
  &lt;span class="c1"&gt;# and executes the custom action we defined above. The payload passes&lt;/span&gt;
  &lt;span class="c1"&gt;# values to the self-hosted scaffolder runner and logs are written&lt;/span&gt;
  &lt;span class="c1"&gt;# back to Roadie.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invokeCustomAction&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invoke a custom self-hosted action&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;roadie:agent&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom-action&lt;/span&gt;
      &lt;span class="na"&gt;shareWorkspace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;someValue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Field extensions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://roadie.io/docs/scaffolder/custom-fields/" rel="noopener noreferrer"&gt;Custom field extensions&lt;/a&gt; give you the ability to build your own inputs and UI components for the scaffolder. You write them using the &lt;a href="https://backstage.io/docs/features/software-templates/writing-custom-field-extensions/" rel="noopener noreferrer"&gt;normal Backstage API&lt;/a&gt;, and push them into Roadie using the custom plugins pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/2lHAUNIJCp4mWEECgnt4js/89dff377a4f75504a0502405005d86ae/Screenshot_2024-06-18_at_12.05.42.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/2lHAUNIJCp4mWEECgnt4js/89dff377a4f75504a0502405005d86ae/Screenshot_2024-06-18_at_12.05.42.png" alt="Customize-the-Roadie-scaffolder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once registered, they can be used in Scaffolder templates by matching the name of the &lt;code&gt;ui:field&lt;/code&gt; prop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fill in some steps&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My custom name for the component&lt;/span&gt;
        &lt;span class="na"&gt;ui:field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MyCustomInput&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authentication &amp;amp; Authorization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Single-Sign-On (SSO) setups
&lt;/h3&gt;

&lt;p&gt;Roadie provides native support for any SSO provider you can imagine. Okta, Microsoft Entra ID, Google, AWS, Ping Identity…. you name it, we’re probably already using it in production.&lt;/p&gt;

&lt;p&gt;Setting this up is a simple matter of sending our docs to your IT team, or whoever controls your SSO setup. We will then work directly with that team to get your authentication provider configured.&lt;/p&gt;

&lt;p&gt;Once set up, your IT team can grant and revoke access to Roadie without being blocked by us. They have full control over the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roles
&lt;/h3&gt;

&lt;p&gt;Customers who have purchased our role-based access control add-on can define custom roles which can then be assigned to groups of users.&lt;/p&gt;

&lt;p&gt;In this example, you can create a role which can only read the catalog. It wouldn’t be able to execute scaffolder templates or register anything in the catalog.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6WAxROTDuksZKP9Hjngzyp/6319b1104af0474c5a2fb93c453d813a/custom-roles.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6WAxROTDuksZKP9Hjngzyp/6319b1104af0474c5a2fb93c453d813a/custom-roles.png" alt="custom-roles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Roles can also be assigned via your Identity Provider like Okta or Microsoft Entra ID. This is especially useful when your IT team wishes to manage access and roles in a single place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissions/policies (coming soon)
&lt;/h3&gt;

&lt;p&gt;When role-based access control gets really powerful is with the ability to define custom permissions which can then be combined in roles. For example, perhaps you want to hide Component entities which are tagged with &lt;code&gt;sensitive&lt;/code&gt; from everyone except the security team.&lt;/p&gt;

&lt;p&gt;We’re working on this ability at Roadie and expect it to roll out in 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Insights (Scorecards)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Custom data sources
&lt;/h3&gt;

&lt;p&gt;Data Sources are recurring jobs which gather data that can then be used to write checks against.&lt;/p&gt;

&lt;p&gt;For example, Roadie comes with a built-in GitHub Settings Data Source which records facts about each repository in the catalog, like whether or not branch protection is turned on, or whether or not force pushes are allowed.&lt;/p&gt;

&lt;p&gt;Users can also create their own Data Sources from scratch. At the time of writing, 7 different types of Data Sources are supported. Here are some examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HTTP Data Sources hit third-party APIs and pick values from the JSON response&lt;/li&gt;
&lt;li&gt;Component Repository File Data Sources inspect files in the repository associated with each Component and record values from them.&lt;/li&gt;
&lt;li&gt;Push Based Data Sources receive webhook events and store them for processing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Creating Data Sources is a simple matter of filling out a few fields. You don’t need to write any TypeScript or YAML. Data Sources support advanced features like metadata variables, JSONata processing, inline testing, and error handling.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/7k85THcrcjLAflDySczrQ4/5fceb794b799ee77bbca4169c7d53d0b/Screenshot_2024-06-18_at_12.43.37.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/7k85THcrcjLAflDySczrQ4/5fceb794b799ee77bbca4169c7d53d0b/Screenshot_2024-06-18_at_12.43.37.png" alt="Custom-data-sources"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom checks
&lt;/h3&gt;

&lt;p&gt;Checks inspect a value created by a Data Source and give it a pass or fail for each entity it applies to.&lt;/p&gt;

&lt;p&gt;This check ensures there is a README.md in the repository associated with each piece of software. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/5QIfu5EIA3phMK07hAn8pJ/4650b11efa61342810c045a201acb4b8/Screenshot_2024-06-18_at_12.45.10.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/5QIfu5EIA3phMK07hAn8pJ/4650b11efa61342810c045a201acb4b8/Screenshot_2024-06-18_at_12.45.10.png" alt="Custom-checks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Advanced features like boolean logic and live testing of the check are supported. Checks can link to documentation when they fail, or even link to a scaffolder template that can help the owner of a service to fix the failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom scorecards
&lt;/h3&gt;

&lt;p&gt;Users can create their own scorecards that apply to the software in the catalog in order to communicate best practices and expectations to teams.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/75CxdnNECkNEfBUzcTRjDM/c4885ac9b50147d9aacea06cc7e3db80/scorecard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/75CxdnNECkNEfBUzcTRjDM/c4885ac9b50147d9aacea06cc7e3db80/scorecard.png" alt="scorecard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each scorecard is a user defined collection of checks that shows up in reporting, in the catalog, and on team pages. Scorecard data is rolled-up through the catalog so that you can dip in at any level of the org chart and see aggregated data from below that point.&lt;/p&gt;

&lt;h2&gt;
  
  
  And there’s more coming
&lt;/h2&gt;

&lt;p&gt;We’re constantly adding to the ways you can customize Roadie. If you want to stay up to date with the latest customizations, &lt;a href="https://roadie.io/backstage-weekly/" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt; to our newsletter to hear what’s coming.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Image by &lt;a href="https://pixabay.com/users/grumpybeere-22072131/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=8829735" rel="noopener noreferrer"&gt;GrumpyBeere&lt;/a&gt; from &lt;a href="https://pixabay.com//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=8829735" rel="noopener noreferrer"&gt;Pixabay&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Backstage and Cost Insights: shifting cloud costs left</title>
      <dc:creator>Jian Reis</dc:creator>
      <pubDate>Wed, 16 Oct 2024 07:31:48 +0000</pubDate>
      <link>https://forem.com/roadie/backstage-and-cost-insights-shifting-cloud-costs-left-5ff9</link>
      <guid>https://forem.com/roadie/backstage-and-cost-insights-shifting-cloud-costs-left-5ff9</guid>
      <description>&lt;h3&gt;
  
  
  Cloud computing - the double-edged sword
&lt;/h3&gt;

&lt;p&gt;Managing cloud costs is fast becoming a strategic priority. While cloud service providers (CSPs) like &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/" rel="noopener noreferrer"&gt;Microsoft Azure&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;Amazon Web Services&lt;/a&gt; offer incredible flexibility and scalability, they can quickly lead to ballooning costs if left unchecked. We’ve all &lt;a href="https://x.com/GergelyOrosz/status/1542449611440328704?lang=en" rel="noopener noreferrer"&gt;seen&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=8927083" rel="noopener noreferrer"&gt;the&lt;/a&gt; &lt;a href="https://newsletter.goodtechthings.com/p/the-cloud-billing-risk-that-scares" rel="noopener noreferrer"&gt;cloud&lt;/a&gt; &lt;a href="https://cloudsoft.io/blog/the-curious-case-of-the-spiralling-aws-lambda-bill" rel="noopener noreferrer"&gt;billing&lt;/a&gt; &lt;a href="https://thenable.io/how-a-recursive-lambda-function-cost-hundreds-of-dollars" rel="noopener noreferrer"&gt;horror&lt;/a&gt; &lt;a href="https://www.troyhunt.com/how-i-got-pwned-by-my-cloud-costs/" rel="noopener noreferrer"&gt;stories&lt;/a&gt;, but even for companies that have a good grip on their cloud costs, it’s easy to get into a situation where cost growth begins to outstrip associated revenue growth. &lt;/p&gt;

&lt;p&gt;Such a position can undermine profitability, growth, and operational flexibility, ultimately putting the organization on an unsustainable trajectory. Beyond simple expense, runaway costs can mask inefficiencies in architecture, resource allocation, and service usage, creating a vicious cycle of wasted consumption. Left unmanaged, this can undermine even the most successful products, ultimately affecting their long term viability. &lt;/p&gt;

&lt;p&gt;A notable case in point is Spotify, who &lt;a href="https://redmonk.com/jgovernor/2021/04/28/shifting-cost-optimisation-left-spotify-backstage-cost-insights/" rel="noopener noreferrer"&gt;encountered this scenario early on&lt;/a&gt; which forced the company to rethink its approach to managing cloud costs. Spotify realized that to get a handle on this, they needed to empower their engineers - the people closest to the actual cloud usage - to own the costs and be responsible for optimizing them. Rather than a top-down approach mandating a cost reduction, Spotify opted to make the engineers themselves responsible for their own cloud costs, an excellent example of the phenomenon of shifting cloud costs left.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shifting cost awareness left: a cultural change
&lt;/h3&gt;

&lt;p&gt;Shifting cost awareness left is a concept in effective cloud cost management that is gaining traction. Essentially a form of embedding cost considerations earlier in the engineering process, not as an afterthought for finance teams, that’s exactly what Spotify did, according to &lt;a href="https://redmonk.com/jgovernor/2021/04/28/shifting-cost-optimisation-left-spotify-backstage-cost-insights/" rel="noopener noreferrer"&gt;James Governor at RedMonk&lt;/a&gt;: “Spotify engineering teams are used to having a lot of autonomy, so the company couldn’t just introduce new cost guardrails as a top down concern. Therefore the Cost team tried to foster a culture where optimization would be fun.” &lt;/p&gt;

&lt;p&gt;Shifting cost awareness left isn’t just about giving engineers tools, it’s a cultural shift where engineers take ownership of the financial impact of their work and are invested in cost optimization. When engineers are empowered with real-time cost insights, they become agents of change. Instead of waiting for an end-of-month cloud bill to identify costly inefficiencies, engineers can make informed decisions in real-time. This shifts cost management left from a reactive process driven by finance to a proactive, engineering-led initiative.&lt;/p&gt;

&lt;p&gt;There’s a philosophical value here that goes beyond dollars and cents. By making cost awareness a core part of the development lifecycle, companies can drive a sense of ownership and even healthy competition among teams. Engineers begin to actively look for ways to optimize their cloud usage, often competing with each other to drive down costs. This culture of cost ownership not only saves money but also leads to better architecture and more efficient systems overall.&lt;/p&gt;

&lt;p&gt;According to Janisa Anandamohan, Spotify Senior Product Manager, Cost Engineering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We know engineers are natural optimizers when it comes to reliability, security, performance, et cetera. And now we’re telling them, hey, add costs into the mix. And they were super excited about that. We had many teams that were just able to tweak their services and data pipelines and to make them more efficient. And we know efficiency doesn’t just help cost, but helps reliability and performance as well. So we were getting double, triple wins along the way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Spotify’s solution: Cost Insights
&lt;/h3&gt;

&lt;p&gt;Spotify’s approach to shifting managing cloud costs left took the form of a cost optimization and visualization plugin called Cost Insights, on their own internal version of Backstage. The concept is simple: provide engineers with the tools to visualize and manage the costs associated with the services they build, all within the internal platform they are already using - their internal developer portal. By tying cloud costs to specific entities in the Backstage catalog, teams are empowered to take control of their spending and optimize resources more effectively.&lt;/p&gt;

&lt;p&gt;This approach worked well for Spotify, partly due to their internal engineering culture, but mostly as a result of the significant engineering effort invested into getting Cost Insights integrated into their developer platform. While they have since &lt;a href="https://github.com/backstage/community-plugins/tree/main/workspaces/cost-insights/plugins/cost-insights" rel="noopener noreferrer"&gt;open-sourced a pared-down version of the Cost Insights plugin&lt;/a&gt; back to the community, most organizations attempting to replicate their success will find it challenging, even when using their plugin. This is largely because of the technical complexity required, and because the current Spotify Cost Insights plugin, like many of the other aspects of the Backstage framework, is far from an out-of-the-box solution. &lt;/p&gt;

&lt;p&gt;Just how much work would it take an engineering team, even working with the open-sourced Cost Insights plugin to implement a working solution? It’s a significant lift; here’s what they’d have to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cloud billing integration&lt;/strong&gt;: Set up a mechanism such as an API to pull cost data. This involves not only access configuration but also understanding the data schema of your cloud provider, which can be complex and time-consuming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend development&lt;/strong&gt;: Develop a backend to fetch, process, and store the billing data. This step requires designing a database schema that can handle potentially large amounts of data efficiently and setting up a server to run this service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Insights API implementation&lt;/strong&gt;: Implement the API endpoints required by the Cost Insights plugin. This includes endpoints for fetching cost data, projecting future costs based on current trends, and breaking down costs by services, projects, or departments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend integration&lt;/strong&gt;: Integrate the Cost Insights plugin into your Backstage instance. This may involve customizing the plugin to fit into your organization’s Backstage environment and ensuring it interacts correctly with your newly developed backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing and validation&lt;/strong&gt;: Thoroughly test the plugin with real data to ensure accuracy. Validate the cost projections and insights with your finance team to ensure they align with actual expenditures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance and updates&lt;/strong&gt;: Continuously update the backend and frontend as cloud providers change their APIs or pricing models, and as new features or fixes become available in the Cost Insights plugin.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a small team of three to four engineers, this could take anything from several months to a year to fully implement. This is a big lift for most organizations, which means that for many, this complexity keeps the solution remains out of reach. While the concept of cost transparency and shifting cost awareness left resonates, the time and effort required to implement and maintain a working Cost Insights tool deters widespread adoption.&lt;/p&gt;

&lt;p&gt;As if it wasn’t hard enough, all of the work above assumes an organization is using only a single CSP in their stack. In reality, any organization that is using multiple CSPs (say, AWS and GCP) faces a thorny additional hurdle: the lack of data homogenization and normalization from CSPs. Each CSP often presents cost data in a different format, making it extremely challenging for organizations to consolidate and interpret this data in a unified manner if they’re using multiple CSPs.&lt;/p&gt;

&lt;p&gt;Fortunately, the recent introduction of the &lt;a href="https://focus.finops.org/" rel="noopener noreferrer"&gt;FOCUS (FinOps Open Cost and Usage Specification)&lt;/a&gt; standard has changed the landscape for the better. Developed as a collaborative effort by the &lt;a href="https://www.finops.org/introduction/what-is-finops/" rel="noopener noreferrer"&gt;FinOps Foundation&lt;/a&gt;, the FOCUS standard aims to normalize the cost and usage data across different CSPs, providing a common format that makes it easier for organizations to integrate and analyze their cloud spending. This standardization is a crucial enabler, simplifying the data integration process and reducing the overhead associated with translating disparate data formats.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing: Roadie’s Cost Insights Plugin
&lt;/h3&gt;

&lt;p&gt;The adoption of the FOCUS standard significantly simplifies the integration of cost data across various cloud platforms. However, the complexity and effort of setting up Cost Insights is still high - which is where we at Roadie saw an opportunity to help. We recognized that the idea of empowering engineers to manage cloud costs was spot on, but the solution needed to be simpler, more accessible, and ready to use out of the box. As such, we’re in the process of refining and enhancing the existing Cost Insights plugin, making it significantly easier to deploy and use right out of the box.&lt;/p&gt;

&lt;p&gt;Our enhanced &lt;a href="https://roadie.io/docs/integrations/cost-insights/" rel="noopener noreferrer"&gt;Cost Insights&lt;/a&gt; plugin builds on Spotify’s version, but aims to reduces the setup friction, allowing engineering teams to access powerful cloud cost insights immediately - no custom backend development required. This streamlined approach means engineers can spend more time optimizing their services and less time managing the infrastructure for cost tracking.&lt;/p&gt;

&lt;p&gt;With Roadie’s plugin, teams can quickly see which services are driving up their cloud costs, how those costs have trended over time, and what actions can be taken to reduce unnecessary spending. The key here is not just providing data but making it accessible and actionable, so engineers can immediately use it to make decisions.&lt;/p&gt;

&lt;p&gt;Roadie’s Cost Insights plugin takes full advantage of the FOCUS standard, ensuring that the data we present is both accurate and comparable across providers. By eliminating discrepancies in how cost data is reported, the plugin enables engineers to gain a clear understanding of their cloud usage without needing to reconcile different data formats.&lt;/p&gt;

&lt;p&gt;The ability to assign costs to specific entities within a Backstage catalog is what we’re most excited by. In traditional cloud billing tools, costs are often presented at a very high level, making it hard to drill down and see what’s truly driving the expenses. By associating costs with individual services and the teams responsible for them, the plugin makes cloud costs real and relatable for developers. When a team sees exactly how much their service is costing, they can no longer ignore it or avoid responsibility - it becomes part of their job to optimize those costs.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/2lkzBoQ0KD8G6n69u1sQY5/6ae8f0ea2d90f6dacc52c97a3c6f64e0/roadie-cost-insights-preview.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/2lkzBoQ0KD8G6n69u1sQY5/6ae8f0ea2d90f6dacc52c97a3c6f64e0/roadie-cost-insights-preview.png" alt="The Roadie Cost Insights dashboard displaying cloud cost by product"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Roadie Cost Insights dashboard displaying cloud cost by product&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Our Cost Insights plugin, currently in internal beta, integrates seamlessly with a Roadie-managed Backstage instance to provide teams with an out-of-the-box solution for tracking and managing cloud costs. Key features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easy setup&lt;/strong&gt;: No complicated setup required - simply connect to your cloud service provider to Roadie Cost Insights via your cloud administration settings or via a secure broker, and allow the Roadie Cost Insights compatibility layer to take care of all the data modeling and translation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project and group-based cost tracking&lt;/strong&gt;: Track cloud costs at the entity, team, or product level, and drill down into specific projects or groups for more detailed insights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trend visualization&lt;/strong&gt;: View cloud cost trends over time, breaking down data by dimensions like products, services, and regions to identify patterns and anomalies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FOCUS standard integration&lt;/strong&gt;: The plugin leverages customer-provided FOCUS data, ensuring consistency and like-for-like comparability across cloud providers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actionable insights&lt;/strong&gt;: Dashboards help engineers take ownership of their services, and immediate action by optimizing those services or reducing over-provisioned resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a team might notice that the costs of a particular product are rising faster than expected. With Roadie’s plugin, they can drill down into the cost breakdowns, identify high-cost services, and take corrective action - such as optimizing resource allocations or reducing usage.&lt;/p&gt;

&lt;p&gt;Roadie Cost Insights also supports multiple regions, meaning a DevOps team managing multiple cloud services in different regions could use Roadie’s Cost Insights to track cloud costs by region. For instance, they could spot that their compute resources in one region are significantly more expensive compared to others. Using the plugin’s trend visualization and breakdown capabilities, the team can identify the services or instances driving up the cost and adjust their architecture to either reduce or move resources to more cost-effective regions.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/1HujxcKSbrPOdrga7Qfbvo/464c993068096fe762c5967d6655c4d8/roadie-cost-insights-architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/1HujxcKSbrPOdrga7Qfbvo/464c993068096fe762c5967d6655c4d8/roadie-cost-insights-architecture.png" alt="Roadie Cost Insights Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Roadie Cost Insights Architecture - note that the user configuration overhead is limited to installing the Cost Insights client on the cloud service provider infrastructure (or the use of a &lt;a href="https://roadie.io/docs/integrations/broker/" rel="noopener noreferrer"&gt;broker client&lt;/a&gt;) while Roadie takes care of all the backend setup.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The future of Roadie’s Cost Insights Plugin
&lt;/h3&gt;

&lt;p&gt;We believe that by simplifying cost management and putting it directly in the hands of engineers, companies can not only save money but also drive innovation, efficiency, and alignment across their teams.&lt;/p&gt;

&lt;p&gt;Cloud cost management is no longer just a financial issue—it’s an engineering one. By providing real-time insights into cloud spending and shifting cost awareness left, Roadie’s Cost Insights plugin helps companies empower their engineering teams to take ownership of their cloud usage. This leads to smarter decisions, lower costs, and more efficient systems.&lt;/p&gt;

&lt;p&gt;While Cost Insights is still currently in an internal beta, if you’re interested in learning more or becoming an early design partner, &lt;a href="https://www.linkedin.com/company/43197350/" rel="noopener noreferrer"&gt;get in touch with us today&lt;/a&gt;. We’d love to work together to bring the future of cloud cost management to your team.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Title image by &lt;a href="https://pixabay.com/users/brianpenny-29844978/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=8533603" rel="noopener noreferrer"&gt;Brian Penny&lt;/a&gt; from &lt;a href="https://pixabay.com//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=8533603" rel="noopener noreferrer"&gt;Pixabay&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Ultimate Guide to Backstage Software Catalog Completeness</title>
      <dc:creator>David Tuite</dc:creator>
      <pubDate>Wed, 09 Oct 2024 10:10:00 +0000</pubDate>
      <link>https://forem.com/roadie/the-ultimate-guide-to-backstage-software-catalog-completeness-4kmh</link>
      <guid>https://forem.com/roadie/the-ultimate-guide-to-backstage-software-catalog-completeness-4kmh</guid>
      <description>&lt;p&gt;Internal developer portals (IDPs) like Backstage and Roadie are, at their core, software catalogs. The software catalog is the backbone upon which much of the other functionality hangs, and building a complete catalog is vital to the success of the IDP project.&lt;/p&gt;

&lt;p&gt;Without a complete catalog, an IDP cannot fulfill it’s primary purpose: improving developer productivity. It cannot drive improved discoverability if the software that teams are trying to discover is not in there. It cannot help to measure the standardization or security posture of software it does not know about.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/1I3jY1seLqybnUXxPtTPHn/7c863cdfb25add62d4c8dd5a684794f1/catalog.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/1I3jY1seLqybnUXxPtTPHn/7c863cdfb25add62d4c8dd5a684794f1/catalog.png" alt="Closeup of Roadie catalog data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article is a comprehensive guide to catalog completeness in Backstage. Roadie is built on top of Backstage, and so all of the same lessons apply. We cover why it’s important, how to measure it, and how to achieve it. &lt;/p&gt;

&lt;p&gt;First, let’s learn why you want to build a complete catalog in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why building a complete catalog is important
&lt;/h2&gt;

&lt;p&gt;The primary reason companies deploy an IDP is because they want to make developers more efficient. A big part of this is helping teams answer basic questions about the software around them. They want to make it easy to answer questions like “do we have a geocoding service?”, “which team owns the checkout service?” and “where is the API spec for the users service?”. These questions can only be answered by the IDP if the geocoding service, checkout service, and users service are registered there in the first place.&lt;/p&gt;

&lt;p&gt;A complete catalog is essential when using an IDP to &lt;a href="https://dev.to/blog/tech-insights-for-roadie-backstage/"&gt;measure the maturity of the software that teams are producing&lt;/a&gt;. Applying scorecards to software in the catalog of the IDP can be a great way to determine the security posture of each production service. But scorecards can only apply to software which is actually in the catalog.&lt;/p&gt;

&lt;p&gt;The software catalog is also the fundamental unit of navigation in the IDP. The lack of a software catalog is the reason that wikis like Confluence or Notion cannot solve the discoverability problem that IDPs solve. In a wiki, content is organized into pages, which are intentionally unstructured and flexible. In an IDP, content is organized by Component (think “piece of software”) and is structured so that the same information is available for each Component.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to measure catalog completeness
&lt;/h2&gt;

&lt;p&gt;Having a complete catalog doesn’t necessarily mean that every piece of software is in the catalog. Most organizations have their share of abandoned code. It may have been created during hackathons or for brief experiments that never saw production usage. Having all of this stuff in the catalog can create clutter and noise.&lt;/p&gt;

&lt;p&gt;There may also be parts of the organization for whom it doesn’t make sense to be in the catalog. We work with companies which have large hardware divisions who write code for embedded devices. They don’t necessarily follow the DevOps lifecycle and thus are sometimes intentionally omitted.&lt;/p&gt;

&lt;p&gt;Production software is the most important stuff to have in the catalog. It’s the software that most engineers work with most of the time. Production software will have the most frequently referenced APIs and docs and it’s much more important to have an understanding of the maturity of the software that runs in production environments, since it has the most attack vectors.&lt;/p&gt;

&lt;p&gt;For this reason, we frequently see customers create a list of software which is deployed to production by referencing ArgoCD or some other deployment tool. They then compare this against software in the catalog, frequently by &lt;a href="https://dev.to/docs/api/catalog/"&gt;accessing the Roadie APIs&lt;/a&gt; or by using our CSV export functions.&lt;/p&gt;

&lt;p&gt;The next best bet is to compare number of pieces of software (aka. Components) in the catalog against the number of active, unarchived repositories in your source code solution. This will never give you a perfect answer, because “pieces of software” don’t necessarily map perfectly one-to-one to repositories (monorepos etc), but it’s a good start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At Roadie we give every customer a chart which shows the percentage of their active (non-archived and received a commit in the past 12 months) repositories against the number of Components in the catalog.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/1ARgOlDcPMyAFeoRNya90q/0d38708de2c23c6b644b3f33d3a95146/Screenshot_2024-09-20_at_13.54.53.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/1ARgOlDcPMyAFeoRNya90q/0d38708de2c23c6b644b3f33d3a95146/Screenshot_2024-09-20_at_13.54.53.png" alt="Catalog completeness formula"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadie customers can achieve high catalog completeness
&lt;/h2&gt;

&lt;p&gt;Achieving a high level of catalog completeness is definitely possible. &lt;/p&gt;

&lt;p&gt;The majority of established Roadie customers are happy with their level of catalog completeness and we have many customers who have a catalog completeness level (measured as a percentage of active repositories which are registered in the catalog) which is above 80%.&lt;/p&gt;

&lt;p&gt;Here’s a chart showing catalog completeness for &lt;a href="https://uplight.com/" rel="noopener noreferrer"&gt;Uplight&lt;/a&gt; who onboarded in September 2023. Four months later they were at 88% catalog completeness, with more than 600 components in their catalog.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/N1n0O0iqaKNeWu1bjhTAc/39ecee5eac4d8dae1729ae981eb82d8d/uplight_catalog_completeness.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/N1n0O0iqaKNeWu1bjhTAc/39ecee5eac4d8dae1729ae981eb82d8d/uplight_catalog_completeness.png" alt="uplight catalog completeness"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s another Roadie customer who increased their catalog completeness from 40% to 90% over a period of four months. They now have more than 750 components registered.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/4ZGSECkA66xHAQgGV2FKgq/801c0f364b46e4c829c88b43547e7bcc/snyk_catalog_completeness.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/4ZGSECkA66xHAQgGV2FKgq/801c0f364b46e4c829c88b43547e7bcc/snyk_catalog_completeness.png" alt="customer catalog completeness"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are countless more examples like this amongst Roadie customers. The goal of this post is to help all companies achieve the same results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for building a complete catalog
&lt;/h2&gt;

&lt;p&gt;There are multiple strategies that can be used to build a complete catalog. The strategy that is right for your company can depend on factors like the size of the company and the enthusiasm of the developers to have an IDP. &lt;/p&gt;

&lt;p&gt;You may also want to run multiple strategies in parallel, or start with one in order to get the bulk of software into the catalog, and switch to another to get closer to full catalog completeness.&lt;/p&gt;

&lt;p&gt;Think about expanding catalog completeness like the layers of an onion. Start with the most enthusiastic early adopters and get them onboard. Then use them as an example as you expand out to the rest of the organization.&lt;/p&gt;

&lt;p&gt;We recommend you import users (aka. employees) and teams (called Groups in Backstage nomenclature) into the catalog before creating any software components. You will ideally want to assign ownership of each component to a team as you import it. This task is much easier if all of your teams already exist in the catalog.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you don’t know which strategy to choose…
&lt;/h3&gt;

&lt;p&gt;Experiment! You don’t need to roll out to the whole organization in one go. Pick some friendly teams, run one strategy on each team, and analyze the results. Did you end up with software in the catalog? What is the feedback from each team? Where did they get stuck? Take this feedback and use it to improve the process before expanding out to more teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 1: Centralized automated
&lt;/h3&gt;

&lt;p&gt;This strategy involves connecting to a chokepoint in the organization in order to push software metadata into the catalog programatically. It does not require anyone to write &lt;code&gt;catalog-info.yaml&lt;/code&gt; files. &lt;/p&gt;

&lt;p&gt;Frequently, the initial chokepoint will not have all of the information required to build a useful catalog. When this happens, the software metadata must be enriched after the fact.&lt;/p&gt;

&lt;p&gt;Depending on your tech stack and permission settings, following options may be viable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An ArgoCD instance which does deployments to the production environment. It has knowledge of the most critical software in the org (software that goes to production) and can thus can be a good starting point for the catalog.&lt;/li&gt;
&lt;li&gt;A Helm chart which is used by a large percentage of the deployable software in the organization.&lt;/li&gt;
&lt;li&gt;A centrally owned CI job or build tool which can be written by the centralized team and applied to software which is owned by other teams in the organization. For example, Lunar Bank have talked about how &lt;a href="https://www.youtube.com/live/a3UjbRse8yk?si=GXheV8QLzHTfPO4G&amp;amp;t=1307" rel="noopener noreferrer"&gt;they populate their catalog from their build tool called shuttle&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A legacy software catalog, developer portal or spreadsheet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The software catalog can quite quickly be bootstrapped to a highly complete state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The ownership link between software and teams is not immediately established.&lt;/li&gt;
&lt;li&gt;Companies with no source of truth or a heavily fractured deployment ecosystem may not be able to implement this strategy.&lt;/li&gt;
&lt;li&gt;The product teams will be less well educated on the value of the IDP when the process is finished.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Tactics to prioritize in order to succeed with this strategy&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use custom entity providers&lt;/li&gt;
&lt;li&gt;Make catalog presence a requirement for deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Strategy 2: Centralized manual
&lt;/h3&gt;

&lt;p&gt;This strategy tries to avoid asking the individual product teams to do work. Instead, the centralized team takes it upon themselves to populate the catalog. They may do this in collaboration with the product teams, but they likely won’t ask them to write any YAML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This strategy is likely to be faster than the distributed manual strategy, especially for companies which don’t have thousands of microservices.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The product teams will be less well educated on the value of the IDP when the process is finished.&lt;/li&gt;
&lt;li&gt;It may be difficult for the centralized team to gather enough data about each individual software component.&lt;/li&gt;
&lt;li&gt;It becomes the centralized teams job to manually keep the catalog up to date.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Tactics to prioritize in order to succeed with this strategy&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store software metadata in a single repository&lt;/li&gt;
&lt;li&gt;Open scripted pull requests&lt;/li&gt;
&lt;li&gt;Customize the catalog nomenclature&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Strategy 3: Distributed manual
&lt;/h3&gt;

&lt;p&gt;This strategy involves asking all of the individual product teams to register the software that they own in the catalog.&lt;/p&gt;

&lt;p&gt;Typically, the central team who own the IDP will meet with and educate the product teams on the value of the IDP, either on an individual basis, or in larger groups. The central team will provide tools and materials to the product teams in order to teach them what they need to do to get their software into the catalog (typically create a &lt;code&gt;catalog-info.yaml&lt;/code&gt; file), and give them clear steps to take in order to achieve catalog completeness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Product teams are more likely to feel a sense of ownership over their software in the catalog because they put it there in the first place.&lt;/li&gt;
&lt;li&gt;Product teams have an opportunity to learn about features of the IDP as they are registering their software. They may then choose to use features in the IDP such as technical documentation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This strategy requires a lot of work from the central team in order to yield high catalog completeness. It will take time. They will need to educate and continually follow up with product teams throughout the company.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Tactics to prioritize in order to succeed with this strategy&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give teams a scaffolder template to register software&lt;/li&gt;
&lt;li&gt;Share catalog completeness numbers publicly&lt;/li&gt;
&lt;li&gt;Tie catalog completeness to a wider initiative&lt;/li&gt;
&lt;li&gt;Write custom plugins to create value&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tactics for building a complete catalog
&lt;/h2&gt;

&lt;p&gt;The tactics you need to use will depend on the strategy you are implementing. This is a full list of all the tactics we know. Please refer to the strategies above in order to know which tactics to choose.&lt;/p&gt;

&lt;p&gt;Some tactics will apply regardless of the strategy that is chosen. They are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customize the catalog taxonomy&lt;/li&gt;
&lt;li&gt;Move onboarding docs into Roadie&lt;/li&gt;
&lt;li&gt;Present on the value of Roadie to people managers&lt;/li&gt;
&lt;li&gt;Tie catalog completeness to a wider initiative&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of the tactics mostly fall into one of the following categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reduce friction for developers who want to get their software into the catalog.&lt;/li&gt;
&lt;li&gt;Create incentive for developers to put their software into the catalog.&lt;/li&gt;
&lt;li&gt;Educate developers on how to use the catalog.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Give teams a scaffolder template to register their software
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Reduce friction&lt;/p&gt;

&lt;p&gt;This involves writing a scaffolder template that teams can use inside Backstage in order to create a &lt;code&gt;catalog-info.yaml&lt;/code&gt; file in their repositories. &lt;/p&gt;

&lt;p&gt;The scaffolder template will ask the user to fill out some information about their software, typically by picking from pre-defined values, and will open a pull request against a repository when finished. Once the user reviews and merges the pull request, auto-discovery will pick up the &lt;code&gt;catalog-info.yaml&lt;/code&gt; file and populate the catalog.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6JZlwjWtlviRJZ447aQSS/981d6528caffed236d0419e717fcf439/scaffolder_template_for_completing_catalog" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6JZlwjWtlviRJZ447aQSS/981d6528caffed236d0419e717fcf439/scaffolder_template_for_completing_catalog" alt="scaffolder template for completing catalog"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Individual developers don’t need to understand the (long) &lt;a href="https://backstage.io/docs/features/software-catalog/descriptor-format" rel="noopener noreferrer"&gt;Backstage YAML API spec&lt;/a&gt; in detail. &lt;/li&gt;
&lt;li&gt;The owners of the IDP can use the template to constrain the “type”, “lifecycle” and other properties that is assigned to each software component. This puts guardrails in place that will help create &lt;a href="/blog/improving-backstage-performance/https://roadie.io/blog/improving-backstage-performance/"&gt;more consistency in the catalog&lt;/a&gt;. Catalog consistency is important, and &lt;a href="https://dev.to/blog/kinds-and-types-in-backstage/"&gt;will help you avoid problems in future&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The form can integrate with external APIs to pull in sensible options. For example, in the screenshot above, the “Component Owner” is a selection of all the engineering teams in the company. The user doesn’t need to type an exact string.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How Roadie helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie provides a &lt;a href="https://github.com/roadie-demo/getting-started/tree/main/scaffolder/register-new-component" rel="noopener noreferrer"&gt;starting point for a software registration template&lt;/a&gt; in the getting-started repo. Customers can fork it into their own GitHub org, edit it to meet their needs and import it into their own Backstage instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Archive unused repositories
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Reduce friction&lt;/p&gt;

&lt;p&gt;If our measure of catalog completeness is:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/1ARgOlDcPMyAFeoRNya90q/0d38708de2c23c6b644b3f33d3a95146/Screenshot_2024-09-20_at_13.54.53.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/1ARgOlDcPMyAFeoRNya90q/0d38708de2c23c6b644b3f33d3a95146/Screenshot_2024-09-20_at_13.54.53.png" alt="Catalog completeness formula"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;then we can increase catalog completeness by archiving old repositories that nobody is using. &lt;/p&gt;

&lt;p&gt;It may sound somewhat silly, but every organization has abandoned hackathon projects and old test repos that are unused and simply causing clutter. By archiving them, we clean up our source code management tool while improving catalog completeness.&lt;/p&gt;

&lt;p&gt;Believe it or not, Roadie has one customer who increased catalog completeness from 45% to 75% just by archiving repositories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our catalog implementation ensures that Components are removed from the catalog when the repo they reference is archived.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customize the catalog nomenclature
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Reduce friction&lt;/p&gt;

&lt;p&gt;By mapping the Kinds of entity that show up in the catalog to familiar concepts in your company, you can create instant recognition for developers who land in the catalog. &lt;/p&gt;

&lt;p&gt;For example, if your company has a concept of “Valuestream” then make this front and center in the catalog so that users instantly understand what they’re looking at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Users can orient themselves quickly and get value rapidly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How Roadie helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie customers can use our admin interfaces to customize and rename the core catalog concepts, and create completely new ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/2c7gYTA1or5GRWYqmu2C2t/236d2036ba43321d81b0acd51a9ab14a/customize_the_catalog.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/2c7gYTA1or5GRWYqmu2C2t/236d2036ba43321d81b0acd51a9ab14a/customize_the_catalog.png" alt="customize the catalog terminology"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Write custom plugins to create value
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Create incentive&lt;/p&gt;

&lt;p&gt;Custom plugins provide value for product teams by giving them faster ways to perform bespoke workflows inside the catalog. By creating custom plugins, a central team can incentivize developers to add their software into the component.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://youtu.be/TZ6-SpoFVeY?si=l-rz2H6HsPzkln0W&amp;amp;t=369" rel="noopener noreferrer"&gt;Lunar Bank have custom plugins for dealing with dead letters in RabbitMQ&lt;/a&gt;. These plugins are regularly useful for developers. This causes them to visit the catalog to use the plugin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Custom plugins are quite simple to produce and can quickly create value for software engineers.&lt;/li&gt;
&lt;li&gt;Custom plugins unlock tailored value for engineers to help them do things more quickly or more easily than they otherwise could. They sometimes even allow them to perform a task that they cannot do at all outside of Roadie or Backstage.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie provides an interface for registering and managing custom plugins. It also facilitates live reloading of custom plugins, the ability to run multiple versions of a plugin side by side, and a &lt;a href="https://github.com/RoadieHQ/software-templates/blob/main/scaffolder-templates/roadie-plugin/template.yaml" rel="noopener noreferrer"&gt;scaffolder template&lt;/a&gt; to bootstrap a custom plugin monorepo. Custom plugins on Roadie can securely connect back to a private network to load data from internal APIs. &lt;a href="https://dev.to/docs/custom-plugins/getting-started/"&gt;Check out our docs to learn more&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Move onboarding docs into Roadie
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Educate&lt;/p&gt;

&lt;p&gt;Engineer onboarding docs are typically used to help a new engineer to set up their environment and get to productivity quickly. Expedia Group, Spotify and other Backstage adopters have successfully used TechDocs and scaffolder templates to speed up engineer onboarding and help new engineers become familiar with the IDP on day one. The exact same tactic can be deployed on Roadie.&lt;/p&gt;

&lt;p&gt;Expedia Group put 850+ engineers through their Backstage based bootcamp in 2022. They discuss it in their &lt;a href="https://backstage.io/blog/2023/08/17/expedia-proof-of-value-metrics-2/" rel="noopener noreferrer"&gt;case study on the Backstage website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Newly onboarded engineers start using Roadie on day one. They get used to it and understand how to come back.&lt;/li&gt;
&lt;li&gt;Engineers learn how to use a scaffolder template to create a new service during onboarding. This service is added to the catalog, and they learn how the catalog works.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie supports standalone TechDocs that are not tied to a particular software component in the catalog. These are perfect for onboarding docs. &lt;/p&gt;

&lt;h3&gt;
  
  
  Present on the value of Roadie to people managers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Educate&lt;/p&gt;

&lt;p&gt;Roadie provides specific value to managers, directors and VPs that is different than the value that developers might care about. By educating managers on the value they will receive, you can encourage them to work with their teams to get their software into the catalog.&lt;/p&gt;

&lt;p&gt;For example, did you know that &lt;a href="https://backstage.spotify.com/blog/how-spotify-measures-backstage-roi/" rel="noopener noreferrer"&gt;frequent Backstage users at Spotify are 5% more likely to be at the company&lt;/a&gt; one year later. Retention is important for managers, so they need to know about this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://dev.to/product/tech-insights/"&gt;Roadie provides Scorecards&lt;/a&gt; which can help managers ensure that their teams are producing mature and high quality software. This feature is not available for open-source Backstage.&lt;/li&gt;
&lt;li&gt;Roadie gives customers value calculators to help them estimate the dollar value they can expect to receive.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Open scripted pull requests
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Reduce friction&lt;/p&gt;

&lt;p&gt;Opening an automated pull request containing a &lt;code&gt;catalog-info.yaml&lt;/code&gt; file is a good way to ease the burden on developers who want to get their software into the catalog. All they need to do is edit the pull request a little bit, review it and merge it.&lt;/p&gt;

&lt;p&gt;Keep in mind that your script will need permissions to open a pull request against a majority of repositories in your source code management tool. Depending on your security model, this may not be possible.&lt;/p&gt;

&lt;p&gt;This method can work especially well in companies that operate out of large monorepos. A monorepo setup allows the generation of a single pull request that can add many &lt;code&gt;catalog-info.yaml&lt;/code&gt; files in one go. It can also be reviewed and merged by a single person with elevated permissions.&lt;/p&gt;

&lt;p&gt;While this is a tempting option to quickly build a catalog with YAML files, we have seen customers experience issues with catalog correctness when they use this method. Some teams may blindly merge the pull request without validating the information that it contains. This tactic should be executed alongside an education program to teach teams what to do. Go slowly and experiment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/docs/api/catalog/"&gt;Roadie provides a token authenticated API&lt;/a&gt; which the centralized team can use to tell which repositories are already in the catalog. A simple script can consume this to open a PR against the repos which are not already accounted for.&lt;/p&gt;

&lt;p&gt;Our solutions engineering team can work with you to write a simple script that will open a pull request containing a YAML file into each repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make catalog presence a requirement for deployment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Create incentive&lt;/p&gt;

&lt;p&gt;By making catalog presence mandatory for deployment, platform teams can be confident that they have all of the important software in the catalog.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/backstage-spotify/#the-origins-of-spotify-backstage"&gt;the early days of Backstage at Spotify&lt;/a&gt;, teams could not SSH into their machines unless their services were in the catalog. The catalog owner was referenced to determine who was and was not allowed to access the machine.&lt;/p&gt;

&lt;p&gt;This tactic works best when the IDP is orchestrating a new greenfield platform that other teams are migrating onto. Adding the &lt;code&gt;catalog-info.yaml&lt;/code&gt; file can be one simple step in what is likely a series of steps that teams have to do to migrate. Outside of this situation, it can be politically problematic to block deployments due to a missing YAML file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate catalog collection with custom entity providers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Reduce friction&lt;/p&gt;

&lt;p&gt;If developers find writing YAML files tedious, potentially the best thing to do is to make them optional. &lt;a href="https://backstage.io/docs/features/software-catalog/external-integrations/" rel="noopener noreferrer"&gt;Backstage’s custom entity providers&lt;/a&gt; allow adopters to programmatically shovel software entries into the catalog. Custom entity providers are a great way to connect Backstage to an existing source of truth for catalog data, such as a legacy IDP, an ArgoCD instance, a kubernetes cluster, or a CICD tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie gives customers the &lt;a href="https://github.com/RoadieHQ/roadie-agent?tab=readme-ov-file#entity-provider" rel="noopener noreferrer"&gt;roadie-agent&lt;/a&gt;. This wraps the custom entity provider concept with a secure connection to Roadie so that customers can easily dump software metadata into the agent and have it appear in the catalog.&lt;/p&gt;

&lt;p&gt;Roadie has an &lt;a href="https://dev.to/docs/api/catalog/"&gt;Entity Provider API&lt;/a&gt;. Simply push an array of software metadata to this endpoint and it will appear in the catalog. To update the metadata, simply push again.&lt;/p&gt;

&lt;p&gt;Frequently the programatic source of truth will have some but not all of the metadata that the catalog needs. For example, it may be missing the name of the team who owns the software. Roadie allows users to &lt;a href="https://dev.to/docs/integrations/github-discovery/#decorating-catalog-entities"&gt;decorate&lt;/a&gt; software in the catalog with extra metadata within the UI. This ensures that the catalog can become complete and rich over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Share catalog completeness numbers publicly
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Create incentive&lt;/p&gt;

&lt;p&gt;One great way to get people bought into the idea of building a complete catalog is to make it a group effort. By transparently reporting on the completeness and “health” of the catalog, a shared sense of ownership over the goal can be created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/P-JMwuuobgY?si=BNihgof1uNBUM2pw&amp;amp;t=1235" rel="noopener noreferrer"&gt;Twilio explained how they do this in their catalog&lt;/a&gt; at the Autodesk Developer Productivity Summit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roadie gives all Tech Insights customers an out-of-the-box measurement of catalog completeness.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/44W4D88ivKswQ5Idg1jZ8W/93f4e9fb1a516a926794a293973c006b/tech_insights_catalog_scorecard.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/44W4D88ivKswQ5Idg1jZ8W/93f4e9fb1a516a926794a293973c006b/tech_insights_catalog_scorecard.png" alt="tech insights catalog scorecard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also report on important aspects of catalog richness, like the percentage of pieces of software in the catalog which have an owner assigned. Customers can use these building blocks to &lt;a href="https://dev.to/docs/tech-insights/tracking-catalog-correctness/mandatory-metadata/"&gt;measure other attributes of the health of their catalog&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tie catalog completeness to a wider initiative
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Category:&lt;/strong&gt; Create incentive&lt;/p&gt;

&lt;p&gt;Companies usually want a complete catalog so that they can accomplish some wider engineering goal. By promoting catalog completeness in service of this wider goal, teams can better understand why it is important to be in the catalog, and why they should help.&lt;/p&gt;

&lt;p&gt;For example, we recently worked with a customer who needed a complete and correct list of all software that had access to their users personally identifiable information (PII). By attaching to this company wide goal and leveraging the influence of the company CTO, the company was able to rapidly label hundreds of software components in Roadie with their PII status.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We run regular customer success meetings with customers to help them identify wider engineering initiatives where Roadie can help accomplish the goal more quickly or more easily. We will then work with teams in order to project manage the delivery of a solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do a whiteboarding session
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;I did some eduction with a team at [company] where we brainstormed the services they wanted to cover and their relationships, this was a whiteboard exercise and I then created the PR's and had them review.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When &lt;a href="https://dev.to/case-studies/from-self-hosted-backstage-to-roadie/"&gt;Paddle started with Backstage&lt;/a&gt;, they realized that they didn’t have an existing source of truth they could lean on to populate their catalog, and they would have to collate it manually.&lt;/p&gt;

&lt;p&gt;They started with a whiteboarding exercise so they could iterate quickly. Meeting with each team in small groups, Ioannis Georgoulas (Director of SRE), led the process. He first spent time brainstorming the services that the groups wanted to catalog, and defining their boundaries and the relationships between them. This information was all collected in a simple document to start.&lt;/p&gt;

&lt;p&gt;Once he had a solid understanding of the service map, Ioannis opened pull requests against each repository with the &lt;code&gt;catalog-info.yaml&lt;/code&gt; file that was needed. All the teams had to do was review and merge it. Because they had participated in the process to gather this information, they were already bought in and could understand the value of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Roadie Helps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We provide frequent customer success calls with every customer through the initial stages of implementation. We’ll partner with your implementors to run these whiteboarding sessions and gather the information you need to be successful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways/Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a high level of catalog completeness in Backstage need not be intimidating. By carefully choosing the strategy that will work at your company, expanding outwards from the most eager adopters first, and communicating widely as you go, you can reach a high level of catalog completeness in a short amount of time.&lt;/p&gt;

&lt;p&gt;Two topics we have not discussed yet, are catalog richness and completeness. These areas go hand in hand with completeness, and work together to ensure that your IDP has the answers that developers need, when they need them.&lt;/p&gt;

&lt;p&gt;We’ll be covering richness and completeness in another article. If you want to be among the first to read it, make sure to subscribe to the newsletter below.&lt;/p&gt;

</description>
      <category>backstage</category>
      <category>developerportal</category>
      <category>platformengineering</category>
      <category>catalog</category>
    </item>
    <item>
      <title>Easier Relationship Mapping in the Backstage Catalog</title>
      <dc:creator>Sam Blausten</dc:creator>
      <pubDate>Mon, 07 Oct 2024 07:45:17 +0000</pubDate>
      <link>https://forem.com/roadie/easier-relationship-mapping-in-the-backstage-catalog-e97</link>
      <guid>https://forem.com/roadie/easier-relationship-mapping-in-the-backstage-catalog-e97</guid>
      <description>&lt;p&gt;In the Backstage software catalog, relationships provide a glue between the different software, systems, resources and people in your organisation. They can allow you to easily answer questions which otherwise might be much harder to find out in a large or rapidly growing organisation. &lt;/p&gt;

&lt;p&gt;Plugins like the Catalog Graph Plugin allow powerful visualisation of these relationship graphs. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6jMaB3WSx6srUkSNTJxY0o/537bbde8c602bf4535f77d51eede5034/view_full_graph.webp" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6jMaB3WSx6srUkSNTJxY0o/537bbde8c602bf4535f77d51eede5034/view_full_graph.webp" alt="Catalog Graph Plugin displaying relationships"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, relationships can become a core piece of information displayed on entity Overview pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/1PdCPYszEjsGOK9JK0xrWM/f7b2376530652c2b5e7d9400b98b0bff/Screenshot_2024-09-06_at_15.52.52.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/1PdCPYszEjsGOK9JK0xrWM/f7b2376530652c2b5e7d9400b98b0bff/Screenshot_2024-09-06_at_15.52.52.png" alt="Relationships list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Relationships can be added in one of two ways - via a YAML file update in an SCM repository, or in a third party source like GitHub Teams or Okta. A YAML update looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;kind: Component
metadata:
&lt;/span&gt;    name: identity-backend
&lt;span class="p"&gt;spec:
&lt;/span&gt;    ...
&lt;span class="gi"&gt;+   dependsOn:
+       - component:default/users-backend
+       - resource:aws/identity-backend-ec2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Restrictive Relationships
&lt;/h3&gt;

&lt;p&gt;There are a very &lt;a href="https://backstage.io/docs/features/software-catalog/descriptor-format/#kind-component" rel="noopener noreferrer"&gt;limited and restrictive set of allowed relationships&lt;/a&gt; between different Kinds in Backstage by default. &lt;strong&gt;Knowing what is allowed&lt;/strong&gt; requires looking up the Backstage documentation each time to &lt;a href="https://backstage.io/docs/features/software-catalog/descriptor-format" rel="noopener noreferrer"&gt;check the schemas&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;For instance, a Domain cannot “depend on” anything in the default Backstage relationship model. However, maybe you have a domain like Shipping that depends on another like Identity. The Identity domain exposes APIs for customer addresses that the Shipping domain breaks without access to. You might even want to directly model the hard dependency on specific external API’s inside the Identity domain so that its easy to identify issues and notify the right people in charge of the Shipping domain when something breaks in those APIs.&lt;/p&gt;

&lt;p&gt;Confusingly, many of the default relationships terms that can be used in the YAML &lt;code&gt;spec&lt;/code&gt; definition don not map directly to the actual relationship in the catalog. For instance if you want to add a &lt;code&gt;partOf&lt;/code&gt; relationship for a Domain to another Domain, you would need to use &lt;code&gt;subdomainOf&lt;/code&gt; . Adding it as an explicit &lt;code&gt;partOf&lt;/code&gt; field would not work unless you add that in the extended processor as we’ll see shortly. Each Kind has its own differing semantics for each relationship type, which needs to be referenced and checked to ensure a relationship is correctly added. &lt;/p&gt;

&lt;h3&gt;
  
  
  Expanding available relationships
&lt;/h3&gt;

&lt;p&gt;Luckily its an easy engineering task to add a processor that can handle more expansive relationships and even new terms for those relationships such as &lt;code&gt;manages/managedBy&lt;/code&gt; for Users. This processor would run in addition to the &lt;code&gt;BuiltinKindsEntityProcessor&lt;/code&gt; that does the default relationship processing for the catalog and would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CatalogProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CatalogProcessorEmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processingResult&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/plugin-catalog-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCompoundEntityRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parseEntityRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RELATION_DEPENDENCY_OF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RELATION_DEPENDS_ON&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/catalog-model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocationSpec&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/plugin-catalog-common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RELATIONSHIP_MANAGED_BY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RELATIONSHIP_MANAGES&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../constants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExtendedRelationshipsProcessor&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;CatalogProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getProcessorName&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ExtendedRelationshipsProcessor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;postProcessEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;_location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LocationSpec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CatalogProcessorEmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCompoundEntityRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doEmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defaultKind&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;defaultNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;outgoingRelation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;incomingRelation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseEntityRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;processingResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outgoingRelation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;processingResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;incomingRelation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Domain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;doEmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;dependsOn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;defaultKind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;System&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;defaultNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;RELATION_DEPENDS_ON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;RELATION_DEPENDENCY_OF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;doEmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;dependencyOf&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;defaultNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;RELATION_DEPENDENCY_OF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;RELATION_DEPENDS_ON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;doEmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;managedBy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;defaultKind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;defaultNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;RELATIONSHIP_MANAGED_BY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;RELATIONSHIP_MANAGES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;doEmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;manages&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;defaultKind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;defaultNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;RELATIONSHIP_MANAGES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;RELATIONSHIP_MANAGED_BY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;doEmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;managedBy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;defaultKind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;defaultNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selfRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;RELATIONSHIP_MANAGED_BY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;RELATIONSHIP_MANAGES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&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;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Questions to consider
&lt;/h3&gt;

&lt;p&gt;There is a rational that restricting the available relationships for each kind in the catalog prevents misuse of relationships by users adding things to the catalog. However it is worth bearing in mind that relationships in the catalog are mostly just syntactic sugar over the same link between two entities. There is nothing different about &lt;code&gt;dependsOn&lt;/code&gt; and &lt;code&gt;partOf&lt;/code&gt; in Backstage except the perceived meaning of these terms. &lt;/p&gt;

&lt;h3&gt;
  
  
  Educating users
&lt;/h3&gt;

&lt;p&gt;Easily accessible documentation on standards and definitions is the best way to educate contributing users to your Backstage catalog. Backstage has its own &lt;a href="https://backstage.io/docs/features/software-catalog/descriptor-format/" rel="noopener noreferrer"&gt;schema docs&lt;/a&gt; and &lt;a href="https://backstage.io/docs/features/software-catalog/well-known-relations" rel="noopener noreferrer"&gt;descriptions on existing relationships&lt;/a&gt; but you can also host internal documentation in a &lt;a href="https://roadie.io/docs/catalog/showing-dependencies/#available-input-relationships" rel="noopener noreferrer"&gt;more compact format&lt;/a&gt; that is easier to reference quickly. &lt;/p&gt;

&lt;h2&gt;
  
  
  What we did to fix it
&lt;/h2&gt;

&lt;p&gt;At Roadie, we’ve added expanded relationships for all kinds in the catalog to make it easier for people to correctly add relationships using the terms they understand. We’ve &lt;a href="https://roadie.io/docs/catalog/showing-dependencies/#available-input-relationships" rel="noopener noreferrer"&gt;documented them in a compact format&lt;/a&gt; and are working on a UI based editor for these relationships so that users do not have to manually edit each YAML file and go through a PR process to build up a comprehensive mapping of dependencies in an organization.&lt;/p&gt;

&lt;p&gt;We've also added a new card that can show all relationships for an entity, regardless of what kind of relationship it is in list format. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/1PdCPYszEjsGOK9JK0xrWM/f7b2376530652c2b5e7d9400b98b0bff/Screenshot_2024-09-06_at_15.52.52.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/1PdCPYszEjsGOK9JK0xrWM/f7b2376530652c2b5e7d9400b98b0bff/Screenshot_2024-09-06_at_15.52.52.png" alt="Relationships list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contact us via &lt;a href="https://discord.gg/jEMesd4ZdK" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; on on this website's chat to find out more about what Roadie can help with or to talk to our engineers about this topic or other issues you might be having adopting Backstage. &lt;/p&gt;

</description>
      <category>backstage</category>
      <category>developerportal</category>
      <category>platformengineering</category>
      <category>relationships</category>
    </item>
    <item>
      <title>Improving Backstage performance (by up to 48x)</title>
      <dc:creator>Brian Fletcher</dc:creator>
      <pubDate>Fri, 04 Oct 2024 07:57:19 +0000</pubDate>
      <link>https://forem.com/roadie/improving-backstage-performance-by-up-to-48x-12d9</link>
      <guid>https://forem.com/roadie/improving-backstage-performance-by-up-to-48x-12d9</guid>
      <description>&lt;p&gt;Backstage is an excellent framework for building an internal developer portal. It provides all of the fundamental building blocks to improve developer experience in an organization.&lt;/p&gt;

&lt;p&gt;Core to Backstage is its Catalog of entities. The Catalog provides a database of software components, resources, libraries, and other kinds of software items. It provides client code and an API backend to retrieve items in the catalog, along with the software interfaces required to populate the entity catalog. Its model is flexible, customizable, and powerful.&lt;/p&gt;

&lt;p&gt;However, with great power comes great responsibility. Without experience, it's easy to develop anti-patterns in Backstage catalog usage. These anti-patterns can then turn into major performance issues at scale. This in turn leads affects trust and usage of the product as a whole.&lt;/p&gt;

&lt;p&gt;At Roadie, we provide an out-of-the-box version of Backstage for our customers. We have come across many of the ways in which non-optimal Catalog client usage can affect performance of the application as a whole. We have seen these performance issues result in lagging page loads and (in extreme cases) causing page loads to fail in Backstage. &lt;/p&gt;

&lt;p&gt;By applying the patterns explained in this post, you could see a huge improvement in Catalog response time. In some cases, you may even see Catalog queries perform 48x faster!&lt;/p&gt;

&lt;h1&gt;
  
  
  Architecture of the Entity catalog
&lt;/h1&gt;

&lt;p&gt;The entity catalog in Backstage is made up of three components. A Catalog client, the Catalog backend, and the Catalog database. When Backstage starts up for the first time, it will have a Catalog database and a catalog backend. When you visit the Backstage application in your browser, it will make use of the Catalog client to retrieve data from the Catalog backend. The Catalog backend in turn retrieves the requested catalog items from the Catalog database.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using the Backstage Catalog Client
&lt;/h1&gt;

&lt;p&gt;Soon after deploying Backstage in an organization, users will want to customize it.&lt;/p&gt;

&lt;p&gt;Frequent customizations we come across include loading entities into the Catalog from an in-house platform or visualizing data from an internal system in the Backstage UI. Customization is normal and is a sign that Backstage is adding value for teams.&lt;/p&gt;

&lt;p&gt;When developers write extensions to Backstage, it's likely they will come across the need to interact with the Catalog. There are two ways they can do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;via a Frontend Backstage extension&lt;/li&gt;
&lt;li&gt;via a Backend Backstage extension&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To make use of the Catalog client in a frontend Backstage extension, you are likely to be using the &lt;code&gt;useApi&lt;/code&gt; hook, along with a &lt;code&gt;useAsync&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;catalogApiRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/plugin-catalog-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/core-plugin-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stringifyEntityRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/catalog-model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useAsync&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-use/lib/useAsync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CustomReactComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;catalogApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;catalogApiRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;catalogApi&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;(&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stringifyEntityRef&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&amp;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 a backend Backstage extension, you are likely to be constructing the Catalog client using the discovery client. The discovery client is a helper that allows plugins to discover the API location of other clients.&lt;/p&gt;

&lt;p&gt;Generally, if you are writing a backend plugin, like a new REST API or a Catalog processor, you will have access to the discovery client. Depending on your particular situation, you may have access to the discovery client in a different way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CatalogClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/catalog-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DiscoveryApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@backstage/core-plugin-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAllEntities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DiscoveryApi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;catalogApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CatalogClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;discoveryApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;You will notice that once you have an instance of the CatalogApi, it is used in the same way in either the frontend or a backend extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;a href="https://backstage.io/docs/reference/catalog-client.catalogapi/" rel="noopener noreferrer"&gt;the Backstage docs&lt;/a&gt; for a more comprehensive explanation of the full Catalog interface.&lt;/p&gt;

&lt;h1&gt;
  
  
  How big can a Backstage Catalog get?
&lt;/h1&gt;

&lt;p&gt;When thinking about Catalog size, it’s useful to think about two things: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How big is each individual entity?&lt;/li&gt;
&lt;li&gt;How many entities do you have?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A typical Entity
&lt;/h3&gt;

&lt;p&gt;A typical Backstage Entity looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backstage.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Component&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artist-web&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The place to be, for great artists&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;example.com/custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom_label_value&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;example.com/service-discovery&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artistweb&lt;/span&gt;
    &lt;span class="na"&gt;circleci.com/project-slug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/example-org/artist-website&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;java&lt;/span&gt;
  &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://admin.example-org.com&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Admin Dashboard&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dashboard&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin-dashboard&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;website&lt;/span&gt;
  &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artist-relations-team&lt;/span&gt;
  &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public-websites&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It describes a website called the &lt;code&gt;artist-web&lt;/code&gt;. It has a few basic Backstage entity properties, and some annotations, tags, and links. It's encoded in YAML here, but in Backstage's database, it is stored as plain text in JSON format.&lt;/p&gt;

&lt;p&gt;Uncompressed, this entity definition is about half a kilobyte. Therefore, a Catalog containing about 20,000 similarly sized entities would add up to about 10 megabytes of data uncompressed. That's a pretty big chunk of data to be sending over the wire.&lt;/p&gt;

&lt;p&gt;However, we haven't seen anything yet…&lt;/p&gt;

&lt;p&gt;The Backstage Catalog model defines an API Kind. These are used to document the endpoints that services make available in Backstage. API entities often contain an embedded OpenAPI doc.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backstage.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artist-api&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retrieve artist details&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openapi&lt;/span&gt;
  &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artist-relations-team&lt;/span&gt;
  &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artist-engagement-portal&lt;/span&gt;
  &lt;span class="c1"&gt;# The embedded OpenAPI spec is in the definition&lt;/span&gt;
  &lt;span class="na"&gt;definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;openapi: "3.0.0"&lt;/span&gt;
    &lt;span class="s"&gt;info:&lt;/span&gt;
      &lt;span class="s"&gt;version: 1.0.0&lt;/span&gt;
      &lt;span class="s"&gt;title: Artist API&lt;/span&gt;
      &lt;span class="s"&gt;license:&lt;/span&gt;
        &lt;span class="s"&gt;name: MIT&lt;/span&gt;
    &lt;span class="s"&gt;servers:&lt;/span&gt;
      &lt;span class="s"&gt;- url: http://artist.spotify.net/v1&lt;/span&gt;
    &lt;span class="s"&gt;paths:&lt;/span&gt;
      &lt;span class="s"&gt;/artists:&lt;/span&gt;
        &lt;span class="s"&gt;get:&lt;/span&gt;
          &lt;span class="s"&gt;summary: List all artists&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At Roadie, we have seen multiple customers with API kind entities in their Catalog with embedded OpenAPI docs as large as 1 megabyte in size. It's easy for even a small-sized engineering organization to have 50 such APIs documented in Backstage. Unoptimized, that's 50MB+ of data that's being transferred every time we query the full Catalog.&lt;/p&gt;

&lt;p&gt;This Catalog size is important because poor use of the Catalog APIs can cause huge database queries and API response sizes to result, which will cause both unwarranted traffic across the network and unwanted wasted time transferring, encoding, and decoding that data.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to Make Good Use of the Entity Catalog
&lt;/h1&gt;

&lt;p&gt;With all this said, we wanted to run through some good practices that are going to help with improving the Backstage experience. The examples we use in the tables below are measured in the browser on a production Catalog that has 14k entities.&lt;/p&gt;

&lt;p&gt;Unoptimized, we're looking at 2.16 seconds and 59.5 MB of data. That's our starting point. Now each experiment we do below is going to improve that data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only query the entities fields that you need
&lt;/h2&gt;

&lt;p&gt;By default, when retrieving entities from the Backstage Catalog, Backstage will return the whole entity for each item listed. As mentioned above, an entity might be as large as 1 megabyte. As such, limiting fields requested to the ones that are strictly required can help a lot. For example, you might have code like the following that is requesting every entity in the Catalog and then converting the result into an array of entity references.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stringifyEntityRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look under the hood, you'll find that the &lt;code&gt;stringifyEntityRef&lt;/code&gt; function only makes use of the kind, name, and namespace. As such, we can cut down on the amount of data transferred across the network by limiting the fields that are requested.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kind&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metadata.name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metadata.namespace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stringifyEntityRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Operation&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catalogClient.getEntities()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.16 seconds&lt;/td&gt;
&lt;td&gt;59.5 Mb&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catalogClient.getEntities({ fields: ['kind', 'metadata.name', 'metadata.namespace'] })&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.767 ms&lt;/td&gt;
&lt;td&gt;1.2 Mb&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Make use of the filter option to retrieve only entities that you need
&lt;/h2&gt;

&lt;p&gt;We have seen a pattern develop whereby the Catalog client is used to retrieve all of the entities in the Catalog, and then the list is filtered client side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entity&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;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is more efficient to send a filter to the catalog client so that filtering is done either in the Backstage backend or the Backstage database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Operation&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catalogClient.getEntities()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.16 seconds&lt;/td&gt;
&lt;td&gt;59.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;await catalogClient.getEntities({ filter: { kind: ['Component'] } })&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;69 milliseconds&lt;/td&gt;
&lt;td&gt;2.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Avoid retrieving all entities in order to count entities
&lt;/h2&gt;

&lt;p&gt;A common pattern we see in Backstage is for developers to download the whole contents of the Catalog in order to count the entities in that Catalog. The following code will cause Backstage to query the database for every entity, then the client will need to decode the JSON it retrieves in order to count the number of entities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;({})).&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a far more performant way to do this, using the query API. The following requests a limit of 1 entity to be returned, and also requests that the &lt;code&gt;uid&lt;/code&gt; field from the entity is the only item that is returned for that entity. The query API always returns the total count of entities for that query. As such it gives us what we need with out downloading the whole Catalog to the client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;catalogClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryEntities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metadata.uid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;totalItems&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The change suggested here is going to save work for the Backstage database, the Backstage backend, and the Backstage frontend.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Operation&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catalogClient.getEntities()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.16 seconds&lt;/td&gt;
&lt;td&gt;59.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catalogClient.queryEntities({ fields: ['metadata.uid'], limit: 1 })&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;45 milliseconds&lt;/td&gt;
&lt;td&gt;0.5 Kb&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Enable Gzip Compression
&lt;/h2&gt;

&lt;p&gt;When not using the Catalog Client, we recommend using &lt;code&gt;gzip&lt;/code&gt; encoding to reduce the amount of data transferred. This is crucial because requests for large amounts of data directly from the Backstage APIs can be massive. Enabling compression significantly decreases the data volume sent to the client. You can achieve this by including the &lt;code&gt;Accept-Encoding&lt;/code&gt; header with your client requests.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Operation&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;curl https://backstage-server/api/catalog/entities&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;59.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;curl https://backstage-server/api/catalog/entities -H 'Accept-Encoding: gzip'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6.7 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep Backstage up to date
&lt;/h2&gt;

&lt;p&gt;The first, and perhaps the most important thing to consider is to keep Backstage up to date. If you are using Roadie, you are already using a very recent version of Backstage. However, if you are managing Backstage yourself, you may have fallen behind. Backstage releases new versions at least once a month, and these versions often contain very valuable performance improvements to the Catalog.&lt;/p&gt;

&lt;p&gt;For example, in version 1.6.7 of the Catalog client library, there was an optimization. Previously, the Catalog client would sort all entities before returning them to the caller. This is a nice, helpful utility until there are thousands of entities to sort. Often, it is not necessary or optimal to receive a sorted list of entities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collaborate on the OSS Backstage core project
&lt;/h2&gt;

&lt;p&gt;As part of research for this document, we spoke with the core maintainers of Backstage, and there are some great ideas about how to continue to improve the performance. For example, it has been discussed that by default, the getEntities function should be replaced by an iterator object. That iterator would be used to page over the list of entities rather than retrieving the whole list.&lt;/p&gt;

&lt;p&gt;As such, keeping up to date with Backstage releases will allow you to benefit from these performance improvements.&lt;/p&gt;

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

&lt;p&gt;This article is illustrative of some of the performance gains that can be achieved, and your mileage may vary. We have not delved into the performance implications of these changes on backend memory and database query performance. However, we can say that these changes can greatly improve these items too. It's difficult to quantify; however, at Roadie, we were seeing huge memory spikes and large garbage collections occurring in Backstage when the whole Catalog is queried. This is possibly due to the physical sizes of the entity Catalogs and the serialization and deserialization that occurs between the client, backend, and database.&lt;/p&gt;

&lt;p&gt;We have shown that making use of some good patterns can result in a much improved load times for users. We have shown some examples where timings are reduced from multiple seconds to sub-second. We have also shown that the sizes sent across the wire can be greatly reduced from multiple megabytes to tens of kilobytes.&lt;/p&gt;

&lt;p&gt;A well-managed and optimized internal developer portal can make your software engineers more efficient and empower them with the information they need. When load times are reduced from multiple seconds to sub 1 second, developers enjoy a fast, responsive experience that means they’re more likely to use Backstage and find what they need.&lt;/p&gt;

</description>
      <category>backstage</category>
      <category>developerportal</category>
      <category>platformengineering</category>
      <category>performance</category>
    </item>
    <item>
      <title>Adopting Backstage - Documentation and Support</title>
      <dc:creator>Jian Reis</dc:creator>
      <pubDate>Thu, 03 Oct 2024 11:02:51 +0000</pubDate>
      <link>https://forem.com/roadie/adopting-backstage-documentation-and-support-5gei</link>
      <guid>https://forem.com/roadie/adopting-backstage-documentation-and-support-5gei</guid>
      <description>&lt;p&gt;This is the first in a series of posts aimed at helping organizations to adopt Backstage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making Backstage Easier for New Users&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine opening Backstage for the first time, searching for a service, and finding... nothing. How do you even search properly? You’d need to understand concepts like entity &lt;a href="https://roadie.io/docs/catalog/modeling-entities/#kinds" rel="noopener noreferrer"&gt;kinds&lt;/a&gt;, &lt;a href="https://roadie.io/docs/catalog/modeling-entities/#types" rel="noopener noreferrer"&gt;types&lt;/a&gt;, and &lt;a href="https://roadie.io/docs/catalog/ownership/" rel="noopener noreferrer"&gt;ownership&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you can't find the entity, how do you add it? This requires knowing your organization’s ingestion patterns: Do you need a YAML file in the code repo? Or do you manually input the URL into the import flow? Without internal support or clear, beginner-friendly getting started documentation, this process can feel like a maze.&lt;/p&gt;

&lt;p&gt;Backstage isn't always intuitive, especially for new users without internal support or clear documentation. Most available open source documentation is aimed at implementers, not end users. It’s often generic, overwhelming, and assumes you’re using GitHub as your SCM system. So, what can you do to make Backstage easier for your team?&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Simplify Backstage for Your End Users
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Invest in User-Friendly Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For Backstage to succeed in your organization, internal getting started documentation to help users use Backstage is a must. This documentation should be front and center for new users, which might mean putting it in an existing documentation platform even if your goal is to eventually move all docs into Backstage’s TechDocs. You can use the &lt;a href="https://backstage.io/docs/getting-started/homepage/" rel="noopener noreferrer"&gt;Homepage plugin&lt;/a&gt; to highlight certain top level info for new users including &lt;a href="https://backstage.io/storybook/?path=/story/plugins-home-components-featureddocscard--default" rel="noopener noreferrer"&gt;a preview card for your getting started docs in TechDocs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Your getting started docs should open with a clear intro to what Backstage is and how it helps. Include simple examples of its core features, remembering that &lt;strong&gt;many new users won’t even know what an internal developer portal is meant to do&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As well as have a intro page in your internal documentation explaining it you could also &lt;strong&gt;publish a blog post introducing IDPs to your engineers&lt;/strong&gt;. &lt;br&gt;
    i.e. Sample content for an intro to IDPs and Backstage&lt;br&gt;
  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### Internal Developer Portals
An Internal Developer Portal (IDP) is an application which is designed to give our developers easy access to information and commonly used workflows. Its goal is to reduce context switching and toil for developers by providing a “single pane of glass” that helps to improve productivity, reduce duplication of effort, and foster a more cohesive and efficient development environment.

An IDP is a place to find information about the software we build and use, the teams who build that software, and the API specs, code repositories and documentation associated with that software. It also typically provides self-service automation scripts for common tasks like creating applications or making changes to infrastructure, and scorecards to help ensure that software is adhering to best practices.

### What is Backstage
Backstage is an open-source platform for building developer portals. It was originally created by Spotify to manage and streamline their complex microservices architecture and was released for public use in 2020. 

**Key features of Backstage include**:
- Software Catalog: A centralized listing of all services, providing an overview and allowing easy management and discovery.
- Software Templates: Streamlined processes for setting up new projects and services with pre-defined templates.
- Plugins: Backstage is extensible with plugins to integrate with various tools and services used in AstraZeneca.
- TechDocs: A documentation site generator that converts Markdown files into a browsable documentation site.

Backstage aims to improve developer productivity and collaboration by providing a single, cohesive interface for all development-related activities.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Identify key user journeys&lt;/strong&gt; and craft your getting started content around them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why would a software engineer or product manager initially visit Backstage?&lt;/li&gt;
&lt;li&gt;What are they trying to accomplish? &lt;/li&gt;
&lt;li&gt;What problems could Backstage help with?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Talk to teams who are just onboarding or align these journeys with company-wide initiatives. If, for example, you see the Scaffolder as a high-value feature, start with docs explaining how to use it and how to modify templates.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/6Hh3EjJGf0hoT9oFXK2aUy/bb31633c50f2784142b86c9a1d0b6806/Screenshot_2024-09-18_at_11.38.53.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/6Hh3EjJGf0hoT9oFXK2aUy/bb31633c50f2784142b86c9a1d0b6806/Screenshot_2024-09-18_at_11.38.53.png" alt="Example documentation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could create these docs in TechDocs and link them from wherever engineers currently go for org-level documentation or just create them in existing documentation systems like Confluence. &lt;/p&gt;

&lt;p&gt;Ensure they are &lt;strong&gt;searchable&lt;/strong&gt; by including the right keywords in titles and descriptions, whether hosted in Backstage or elsewhere.&lt;/p&gt;

&lt;p&gt;Lastly, publicise these docs as much as you can - get a few blog posts out on any internal news distribution channels, ping organization wide channels with the link and a short teaser description (we’ll be writing a more detailed post about internal marketing very soon with a bunch of resources for you to use). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a Dedicated Support Channel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Establish a clear support channel—like a Slack or Teams group—where users can ask Backstage-related questions. This not only helps with adoption but also builds &lt;strong&gt;a community where knowledge is shared and best practices emerge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/7knkX68JmhePqW2cFGY2o0/e2444660df487392de08d0587bb1e979/Screenshot_2024-09-18_at_11.35.19.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/7knkX68JmhePqW2cFGY2o0/e2444660df487392de08d0587bb1e979/Screenshot_2024-09-18_at_11.35.19.png" alt="Support channel example in Slack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pin relevant internal and external docs in these channels to avoid repeated questions. Support channels are also a great place to &lt;strong&gt;identify gaps in your documentation&lt;/strong&gt;, allowing you to improve onboarding and make it as self-service as possible.&lt;/p&gt;

&lt;p&gt;You could even &lt;strong&gt;nominate Backstage champions—advocates&lt;/strong&gt; within your organization who can help answer questions and lighten the load on your Platform Engineering/DevOps teams. &lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/hcqpbvoqhwhm/63QZpSEMwBDMu2rUikNYi4/ae9d4685392ef15ff35f39f1545d8929/Screenshot_2024-09-18_at_12.33.11.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/hcqpbvoqhwhm/63QZpSEMwBDMu2rUikNYi4/ae9d4685392ef15ff35f39f1545d8929/Screenshot_2024-09-18_at_12.33.11.png" alt="Support Champion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At Roadie, support channels with our customers have been essential to successful Backstage rollouts. While documentation is the first line of defense, having a place for follow-up questions is key to encouraging usage and reducing friction for busy engineers.&lt;/p&gt;




&lt;p&gt;By streamlining these two areas—&lt;strong&gt;user-friendly getting started documentation&lt;/strong&gt; and a &lt;strong&gt;dedicated support channel&lt;/strong&gt;—you’ll make engaging with Backstage a smoother experience for your team and boost adoption.&lt;/p&gt;

</description>
      <category>backstage</category>
      <category>developerportal</category>
      <category>documentation</category>
      <category>platformengineering</category>
    </item>
  </channel>
</rss>
