<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sam Blausten</title>
    <description>The latest articles on DEV Community by Sam Blausten (@sam_blausten_ad9891c7a528).</description>
    <link>https://dev.to/sam_blausten_ad9891c7a528</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2161612%2Fbda3d4fa-86b6-41c4-a8ef-13519405517a.jpg</url>
      <title>DEV Community: Sam Blausten</title>
      <link>https://dev.to/sam_blausten_ad9891c7a528</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sam_blausten_ad9891c7a528"/>
    <language>en</language>
    <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://dev.to/roadie/update-the-backstage-catalog-instantly-without-touching-any-yaml-4j25</link>
      <guid>https://dev.to/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>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://dev.to/roadie/easier-relationship-mapping-in-the-backstage-catalog-e97</link>
      <guid>https://dev.to/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>
  </channel>
</rss>
