<?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: sarabadu</title>
    <description>The latest articles on DEV Community by sarabadu (@sarabadu).</description>
    <link>https://dev.to/sarabadu</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%2F11471%2Fc34eac80-9469-415d-aea8-33e64fa9b584.jpeg</url>
      <title>DEV Community: sarabadu</title>
      <link>https://dev.to/sarabadu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarabadu"/>
    <language>en</language>
    <item>
      <title>Backstage logbook: Migrating the Catalog Plugin to the New Frontend System</title>
      <dc:creator>sarabadu</dc:creator>
      <pubDate>Sun, 15 Mar 2026 23:14:56 +0000</pubDate>
      <link>https://dev.to/sarabadu/backstage-logbook-migrating-the-catalog-plugin-to-the-new-frontend-system-f6</link>
      <guid>https://dev.to/sarabadu/backstage-logbook-migrating-the-catalog-plugin-to-the-new-frontend-system-f6</guid>
      <description>&lt;h2&gt;
  
  
  Before we start: where are we?
&lt;/h2&gt;

&lt;p&gt;This post assumes you've already done the initial hybrid-mode migration — switching &lt;code&gt;createApp&lt;/code&gt; to the new version, wrapping legacy options and your root component with the compatibility helpers, and confirming the app still boots. If you haven't done that yet, follow the &lt;a href="https://backstage.io/docs/frontend-system/building-apps/migrating" rel="noopener noreferrer"&gt;official migration guide&lt;/a&gt; first and come back. That part is fairly mechanical and the docs cover it well.&lt;/p&gt;

&lt;p&gt;Once your app is running in hybrid mode, you have something like this: a &lt;code&gt;FlatRoutes&lt;/code&gt; tree that still manually wires up all your routes (including the catalog ones), and a large &lt;code&gt;EntityPage.tsx&lt;/code&gt; that manually composes tabs and cards for every entity kind. At this point, &lt;code&gt;convertLegacyAppRoot&lt;/code&gt; is doing the heavy lifting of keeping things working while we incrementally replace them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Drop in the new Catalog plugin
&lt;/h2&gt;

&lt;p&gt;The first real NFS(new frontend system) step is to import the catalog plugin from its alpha entrypoint and add it to your &lt;code&gt;createApp&lt;/code&gt; features list:&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="nx"&gt;catalogPlugin&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/alpha&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;catalogPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;convertedOptionsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// from the hybrid-mode migration&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;convertedRootFeatures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// from the hybrid-mode migration&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;Once that's in place, you can add the current entityPage to the convertLegacyAppRoot 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;convertedRootFeatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertLegacyAppRoot&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;entityPage&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And remove the legacy catalog routes from your &lt;code&gt;FlatRoutes&lt;/code&gt; tree:&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;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FlatRoutes&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* remove these two */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;Route path="/catalog" element={&amp;lt;CatalogIndexPage /&amp;gt;} /&amp;gt;
    &amp;lt;Route
      path="/catalog/:namespace/:kind/:name"
      element={&amp;lt;CatalogEntityPage /&amp;gt;}
    &amp;gt;
      {entityPage}
    &amp;lt;/Route&amp;gt; */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ...the rest of your routes stay for now */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/settings"&lt;/span&gt; &lt;span class="na"&gt;element&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;&lt;/span&gt;&lt;span class="nc"&gt;UserSettingsPage&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;FlatRoutes&lt;/span&gt;&lt;span class="p"&gt;&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;At this point, you can navigate to &lt;code&gt;/catalog&lt;/code&gt; and see the index page, and you can click into entity pages too. But the entity page probably looks a bit odd, maybe even broken. That's expected, and it's exactly what we're going to fix.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1.1: Understand what just happened
&lt;/h3&gt;

&lt;p&gt;Here's the key thing to understand about the new frontend system. In the legacy world, plugins handed you a bag of React components — &lt;code&gt;EntityAboutCard&lt;/code&gt;, &lt;code&gt;EntityLinksCard&lt;/code&gt;, and so on — and it was your job to compose them in &lt;code&gt;EntityPage.tsx&lt;/code&gt;. You were the integrator.&lt;/p&gt;

&lt;p&gt;In the new system, plugins declare their own extensions and those extensions are added directly to the extension tree. The catalog plugin already ships with extensions for the most common cards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;entity-content:catalog/overview&lt;/code&gt; — the overview tab for all entities&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entity-card:catalog/about&lt;/code&gt; — replaces &lt;code&gt;EntityAboutCard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entity-card:catalog/links&lt;/code&gt; — replaces &lt;code&gt;EntityLinksCard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entity-card:catalog/labels&lt;/code&gt; — replaces &lt;code&gt;EntityLabelsCard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;...&lt;a href="https://github.com/backstage/backstage/blob/master/plugins/catalog/src/alpha/entityCards.tsx" rel="noopener noreferrer"&gt;and several more&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So right now, with &lt;code&gt;convertLegacyAppRoot&lt;/code&gt; still processing your old &lt;code&gt;EntityPage.tsx&lt;/code&gt;, you're rendering both the new plugin-provided cards &lt;em&gt;and&lt;/em&gt; the legacy ones. Duplicate about cards, duplicate links cards — the works.&lt;/p&gt;

&lt;p&gt;The same thing happened to the catalog index page: it's now coming entirely from the plugin, which means any customizations you had — custom columns, custom actions, initial filters — are gone for now. We'll address how to bring those back in the Catalog Index Page section.&lt;/p&gt;

&lt;p&gt;This is also a good moment to add &lt;a href="https://github.com/backstage/backstage/tree/master/plugins/app-visualizer" rel="noopener noreferrer"&gt;&lt;code&gt;@backstage/plugin-app-visualizer&lt;/code&gt;&lt;/a&gt; to your app. Navigate to &lt;code&gt;/visualizer&lt;/code&gt; and you'll get a full tree view of every extension in the system — what's enabled, what's disabled, how they're connected. It's invaluable for understanding how all these pieces couple together before you start pulling the legacy code apart.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Entity Pages Overview cards
&lt;/h2&gt;

&lt;p&gt;This is the most involved part of the migration. The entity page in a typical Backstage app is a large hand-composed JSX tree — different layouts and tabs per entity kind. In the new frontend system, all of that gets replaced piece by piece with extensions. Here's how.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2.1: Clean up the duplicate cards
&lt;/h3&gt;

&lt;p&gt;Go through your &lt;code&gt;EntityPage.tsx&lt;/code&gt; and remove the cards that the catalog plugin now provides natively. For a typical overview page, that might look 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overviewContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Grid&lt;/span&gt; &lt;span class="na"&gt;container&lt;/span&gt; &lt;span class="na"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stretch"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Remove entityWarningContent — the default layout now handles this */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Remove EntityAboutCard — catalog plugin provides entity-card:catalog/about */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Keep: EntityCatalogGraphCard — not yet provided by catalog plugin */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Grid&lt;/span&gt; &lt;span class="na"&gt;item&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EntityCatalogGraphCard&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"gridItem"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Keep: your custom cards */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Grid&lt;/span&gt; &lt;span class="na"&gt;item&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EntityCustomCard&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Remove EntityLinksCard — catalog plugin provides entity-card:catalog/links */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Remove EntityHasSubcomponentsCard — catalog plugin provides this */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;&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;The only items you keep here are the ones that &lt;em&gt;aren't&lt;/em&gt; provided by the catalog plugin, cards from other plugins, and anything you built yourself. Things could still look a bit out of order at this point, but that's okay, we will arrange things later.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2.2: Bring in other plugins as NFS features
&lt;/h3&gt;

&lt;p&gt;Some of the cards you kept around are provided by other Backstage plugins, just not yet added in the new way. For example, &lt;code&gt;EntityCatalogGraphCard&lt;/code&gt; comes from the catalog-graph plugin. Add it the same way you added the catalog 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;import&lt;/span&gt; &lt;span class="nx"&gt;catalogGraph&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-graph/alpha&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;catalogPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;catalogGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// adds entity-card:catalog-graph/relations and friends&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;convertedRootFeatures&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;Then remove the corresponding card from your legacy entity page. Keep doing this for every plugin that has an &lt;code&gt;/alpha&lt;/code&gt; NFS export. You'll be surprised how much of your &lt;code&gt;EntityPage.tsx&lt;/code&gt; just... evaporates.&lt;/p&gt;

&lt;p&gt;If a third-party plugin you depend on doesn't have an NFS release yet, you can try converting it yourself following the &lt;a href="https://backstage.io/docs/frontend-system/building-apps/plugin-conversion" rel="noopener noreferrer"&gt;plugin conversion guide&lt;/a&gt;. And don't forget to ping the maintainers 😄&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2.3: Convert your custom cards into extensions
&lt;/h3&gt;

&lt;p&gt;Whatever is left after the plugin cleanup is either a card you built in-house or something that doesn't have an NFS equivalent yet. For in-house cards, you need to create an &lt;strong&gt;extension&lt;/strong&gt; using &lt;code&gt;EntityCardBlueprint&lt;/code&gt; from &lt;code&gt;@backstage/plugin-catalog-react/alpha&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a straightforward example:&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;createFrontendModule&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/frontend-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;EntityCardBlueprint&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/alpha&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;customCardExtension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EntityCardBlueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&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-internal-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;entity&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;Component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loader&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="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;./components/EntityCustomCard&lt;/span&gt;&lt;span class="dl"&gt;'&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;m&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EntityCustomCard&lt;/span&gt; &lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myCustomModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFrontendModule&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;catalog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customCardExtension&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;Then add &lt;code&gt;myCustomModule&lt;/code&gt; to your &lt;code&gt;features&lt;/code&gt; list in &lt;code&gt;createApp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;filter&lt;/code&gt; prop is how you scope a card to specific entity kinds (or any other condition you want). The &lt;code&gt;type&lt;/code&gt; controls where in the layout the card appears: &lt;code&gt;'info'&lt;/code&gt; places the card in the narrow right-hand info sticky column (like the default About and Links cards), while &lt;code&gt;'content'&lt;/code&gt; spans the full width of the main area. When in doubt, check how similar cards look in the &lt;code&gt;/visualizer&lt;/code&gt; and match accordingly.&lt;/p&gt;

&lt;p&gt;If your card needs configurable props (say, a title the operator can set per-deployment), you can use &lt;code&gt;makeWithOverrides&lt;/code&gt; to define a config schema:&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;z&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;zod&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;EntityCardBlueprint&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/alpha&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;customEntityCardExtension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EntityCardBlueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeWithOverrides&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-internal-card&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&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;Title for the custom card&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="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalFactory&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;originalFactory&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="nx"&gt;entity&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;Component&lt;/span&gt;&lt;span class="dl"&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;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;loader&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="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;./components/EntityCustomCard&lt;/span&gt;&lt;span class="dl"&gt;'&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;m&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EntityCustomCard&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;title&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom Card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the card title can be overridden per environment via &lt;code&gt;app-config.yaml&lt;/code&gt; — no code change needed.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2.4: Control ordering and per-card config
&lt;/h3&gt;

&lt;p&gt;Once all the cards are coming through extensions, you'll probably notice the order isn't quite what you had before. Extensions render in the order they're declared in your config, so you can control this from &lt;code&gt;app-config.yaml&lt;/code&gt;:&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;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entity-card:catalog-graph/relations"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entity-card:catalog/depends-on-components"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# you can also disable cards you don't want showing up&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entity-card:catalog/about"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entity-card:catalog/custom-internal-card"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;config&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;content&lt;/span&gt; &lt;span class="c1"&gt;# we can still override config options&lt;/span&gt;
          &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Custom&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Internal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Card"&lt;/span&gt; &lt;span class="c1"&gt;# our config option can be set here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple of things to note here: you can &lt;code&gt;disabled: true&lt;/code&gt; any card you don't want showing up, and you can pass configuration to the cards that expose a config schema (like the one we built with &lt;code&gt;makeWithOverrides&lt;/code&gt;). To find the exact extension names, I'd recommend using the &lt;code&gt;/visualizer&lt;/code&gt; but basically it's &lt;code&gt;entity-card:${pluginId}/${cardName}&lt;/code&gt; for cards, and &lt;code&gt;entity-content:${pluginId}/${contentName}&lt;/code&gt; for content extensions.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2.5: Custom layouts per entity kind
&lt;/h3&gt;

&lt;p&gt;The default entity layout looks quite nice, but if you had a completely custom grid arrangement for specific entity kinds, you can replicate that with a layout extension.&lt;/p&gt;

&lt;p&gt;First, create your layout component. It receives a &lt;code&gt;cards&lt;/code&gt; prop that you can render however you want:&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;EntityContentLayoutProps&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/alpha&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;CustomEntityLayout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EntityContentLayoutProps&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Custom Entity Layout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cards&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;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a minimal example — in practice you'd want to replicate the warning/error components that the default layout handles. Take a look at the &lt;a href="https://github.com/backstage/backstage/blob/1e2c57ebb0e652b6e30de88a17949a7f5a21fc7a/plugins/catalog/src/alpha/DefaultEntityContentLayout.tsx#L139" rel="noopener noreferrer"&gt;default layout implementation&lt;/a&gt; for inspiration.&lt;/p&gt;

&lt;p&gt;Then wrap it in a blueprint and add it to your module:&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;EntityContentLayoutBlueprint&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/alpha&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;componentLayout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EntityContentLayoutBlueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&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-layout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;entity&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;Component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loader&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="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;./components/catalog/CustomEntityLayout&lt;/span&gt;&lt;span class="dl"&gt;'&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;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CustomEntityLayout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;filter&lt;/code&gt; here means this layout only applies to Component entities. Any entity kind not matched will fall back to the default layout.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Entity Page Tabs and Custom Content
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 3.1: Custom tabs and content
&lt;/h3&gt;

&lt;p&gt;The entity page overview tab isn't the only thing that lives in &lt;code&gt;EntityPage.tsx&lt;/code&gt;. You probably also have tabs for CI/CD, documentation, Kubernetes, and so on. The approach here is the same: if the plugin supporting that tab has an NFS export, import it and remove the tab from the legacy entity page.&lt;/p&gt;

&lt;p&gt;For Kubernetes, for example:&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="nx"&gt;kubernetes&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-kubernetes/alpha&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;features&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;convertedRootFeatures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;catalogPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;kubernetes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// adds the Kubernetes tab automatically&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;Then remove the &lt;code&gt;EntityLayout.Route&lt;/code&gt; for Kubernetes from your legacy entity page — the plugin now owns it.&lt;/p&gt;

&lt;p&gt;For tabs that don't have an NFS plugin yet, or for truly custom content you built in-house, use &lt;code&gt;EntityContentBlueprint&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EntityContentBlueprint&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/alpha&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;customEntityContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EntityContentBlueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&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-content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;entity&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;Component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;website&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&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 Tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deployment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;laptop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loader&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="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;./components/catalog/CustomEntityContent&lt;/span&gt;&lt;span class="dl"&gt;'&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;m&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CustomEntityContent&lt;/span&gt; &lt;span class="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;group&lt;/code&gt; and &lt;code&gt;icon&lt;/code&gt; fields control how the tab appears in the entity page navigation. Tab ordering and grouping can also be tuned through config — the &lt;a href="https://backstage.io/docs/features/software-catalog/catalog-customization#configure-groups-titles-and-icons" rel="noopener noreferrer"&gt;Backstage docs&lt;/a&gt; have the full details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One thing worth flagging: at the time of writing, I did not find a way to feature-flag a content extension in whole. There's &lt;a href="https://github.com/backstage/backstage/issues/31220" rel="noopener noreferrer"&gt;an open discussion&lt;/a&gt; about this in the Backstage repo if you need to follow along.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4: Catalog Index Page
&lt;/h2&gt;

&lt;p&gt;With the entity pages sorted, let's look at what we can recover. Many of the props you used to pass directly to &lt;code&gt;&amp;lt;CatalogIndexPage /&amp;gt;&lt;/code&gt; now map to config options on their corresponding extensions. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initialKind&lt;/code&gt; → &lt;code&gt;catalog-filter:catalog/kind&lt;/code&gt; config &lt;code&gt;initialFilter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ownerPickerMode&lt;/code&gt; → &lt;code&gt;catalog-filter:catalog/mode&lt;/code&gt; config &lt;code&gt;mode&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;initiallySelectedFilter&lt;/code&gt; → &lt;code&gt;catalog-filter:catalog/list&lt;/code&gt; config &lt;code&gt;initialFilter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pagination&lt;/code&gt; → &lt;code&gt;page:catalog&lt;/code&gt; config &lt;code&gt;pagination&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of passing those as JSX props, you configure them per extension:&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;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;page:catalog"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;pagination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cursor&lt;/span&gt;
            &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catalog-filter:catalog/mode"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;owners-only&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catalog-filter:catalog/kind"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;initialFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;system&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catalog-filter:catalog/list"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;initialFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the same way we had with cards, the order of the filter entries in the config also controls the order they appear in the UI.&lt;/p&gt;

&lt;p&gt;If you had &lt;a href="https://backstage.io/docs/features/software-catalog/catalog-customization#custom-filters" rel="noopener noreferrer"&gt;custom filters &lt;/a&gt;built in-house, those become extensions too, using &lt;code&gt;CatalogFilterBlueprint&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CatalogFilterBlueprint&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/alpha&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;customCatalogFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CatalogFilterBlueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&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-catalog-filter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;loader&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="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;./components/catalog/CustomCatalogFilter&lt;/span&gt;&lt;span class="dl"&gt;'&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;m&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CustomCatalogFilter&lt;/span&gt; &lt;span class="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more advanced catalog page customizations like custom table columns and action buttons, there aren't dedicated extension points yet. If you need those you can override the whole page 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customCatalogIndexPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;catalogPlugin&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page:catalog&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;override&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/catalog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;loader&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CatalogIndexPage&lt;/span&gt;
          &lt;span class="na"&gt;initialKind&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Component"&lt;/span&gt;
          &lt;span class="na"&gt;ownerPickerMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt;
          &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;customColumnFunc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;tableOptions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you an escape hatch when the standard config options aren't enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The catalog plugin is probably the most involved NFS migration you'll do, it sits at the center of the entity page, it has its own layout system, and it exposes more extension points than most other plugins. That's exactly why I picked it as a learning ground.&lt;/p&gt;

&lt;p&gt;The mental shift that helped me most: you're no longer the composer. In the legacy system, you assembled all the pieces in React. In NFS, each plugin brings its own pieces and your job is to configure and override rather than wire. Once that clicks, the whole migration approach starts to click.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;All the code shown in this post is based on a scaffolded Backstage app I set up specifically to test and validate each step of this migration: &lt;a href="https://github.com/Sarabadu/backstage-catalog-migration" rel="noopener noreferrer"&gt;Sarabadu/backstage-catalog-migration&lt;/a&gt;. It's not a production app — just a clean starting point to try things out without breaking anything real. If something in the post isn't clicking, looking at the repository might help.&lt;/p&gt;




&lt;h2&gt;
  
  
  Did this help?
&lt;/h2&gt;

&lt;p&gt;If you went through this migration yourself, I'd love to hear how it went. Did something not work the way I described? Did you run into edge cases I missed? And what would you like to see covered next — maybe migrating the other plugin, converting a third-party plugin that doesn't have an NFS export yet, or going deeper on writing custom blueprints?&lt;/p&gt;

&lt;p&gt;Drop a comment below or reach out directly. The more feedback I get, the more useful the next one can be.&lt;/p&gt;

</description>
      <category>backstage</category>
      <category>nfs</category>
      <category>migrations</category>
    </item>
    <item>
      <title>AI fluff generation</title>
      <dc:creator>sarabadu</dc:creator>
      <pubDate>Wed, 27 Aug 2025 21:49:53 +0000</pubDate>
      <link>https://dev.to/sarabadu/ai-fluff-generation-34gb</link>
      <guid>https://dev.to/sarabadu/ai-fluff-generation-34gb</guid>
      <description>&lt;p&gt;I've never been too picky on PR descriptions, but getting good context and details in a PR is a lovely experience. Now AI (Github copilot) gives you that experience with one click, right? RIGHT?&lt;/p&gt;

&lt;p&gt;Well sometimes, maybe... but many times what you get is a detailed summary of what changed in each file and that is something I already have in a different tab, I see a lot of text but still gives me no value.&lt;/p&gt;

&lt;p&gt;This happens mostly on the GH web interface while editing the PR description, and it is understandable in that instance the only context available is the files being modified, and commit messages, not much more. but the most valuable information inside a PR description is maybe the "Why" are we doing this.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the fluff looks like
&lt;/h2&gt;

&lt;p&gt;If you ask an llm to generate text, It will by default add some fluff to it: unnecessary data, fancy wording, and unchecked facts.&lt;/p&gt;

&lt;p&gt;This means you can easily end up with fluffy content everywhere.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"add test" -&amp;gt; ends up with 20 test you hardly read or follow&lt;/li&gt;
&lt;li&gt;"write the commit message" -&amp;gt; end in "improvements and refactors in file X"&lt;/li&gt;
&lt;li&gt;"Writing social media post" -&amp;gt; fancy words everywhere&lt;/li&gt;
&lt;li&gt;"user story"... who knows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bigger issue with this fluff is getting the perception of value, but not real value. Your 20 test maybe test the same thing, or miss the actual business edge cases logic you need, but there are 20 of them and all of them are green!!! That LinkedIn post looks really professional you can remove some em dashes and nobody will notice is not you the one talking there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking the control back
&lt;/h2&gt;

&lt;p&gt;After all this we need to filter the fluff to get the real value out and take the control back eventually. So why not try to work fluff free from the beginning.&lt;/p&gt;

&lt;p&gt;I love to use llm on all this tasks, are really handy. So I look strategies to keep control.&lt;/p&gt;

&lt;p&gt;While writing content like LinkedIn, PR descriptions, Documentation, and this post, I like to write it first and then ask for pointing out improvements or typos, but always being explicit on "Do not re-write it, just explain what to improve". This in my experience give a lot of value by correcting some things, and challenge my text but without losing my identity and the control of what I'm writing&lt;/p&gt;

&lt;p&gt;While coding I like to use agents in this way:&lt;br&gt;
1- I start explain what Im trying to do, my objective, and some indications on how I'm thinking to do it&lt;br&gt;
2- I usually ask for "create a plan" and "do it step by step waiting for my input between steps"&lt;br&gt;
3- Then I challenge the plan or ask the llm agent to challenge my plan until I find a plan that makes sense for me&lt;br&gt;
4- Finally i execute the plan step by step&lt;/p&gt;

&lt;p&gt;When exploring new usages or tools using llm agents, I always try to find this way to keep in control and fluff free. It looks slower at first but at the end is much better to get a controlled result rather that getting a lot of "content" but some personal debt&lt;/p&gt;

&lt;p&gt;Many times I hear "It was (copilot|cursor|the AI)", I like to think on "It was me/us using (copilot|cursor|the AI)" and take the responsibility of my content&lt;/p&gt;

&lt;p&gt;"But I like to vibe code", I understand I like it too, it's great in some situations like exploring ideas, trying out things, or work in hackathon mode, but eventually I need to get the control back.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>experiences</category>
    </item>
    <item>
      <title>What is blocking us from contributing more</title>
      <dc:creator>sarabadu</dc:creator>
      <pubDate>Thu, 21 Aug 2025 14:33:00 +0000</pubDate>
      <link>https://dev.to/sarabadu/what-is-blocking-us-from-contributing-more-4nmc</link>
      <guid>https://dev.to/sarabadu/what-is-blocking-us-from-contributing-more-4nmc</guid>
      <description>&lt;p&gt;I have been encouraging colleagues to contribute to open source for some time, but there’s always a lot of friction. And the friction not only exists in open source; sometimes it is even harder for developers to contribute to internal projects.&lt;/p&gt;

&lt;p&gt;In my experience those friction points fall into these categories&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Friction point 1: I'm not the owner
&lt;/h2&gt;

&lt;p&gt;While working on internal tools and libraries shared across organisations, I often get Slack messages or comments like:: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;- You can improve X, this way.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- Ohh good point, could you raise a PR on that repo, please?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- No worries, I will create a ticket on your board and wait.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;- Ok, :sadcat:&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes people get blocked by those changes, but still prefer to wait before trying to help with some changes. After digging into why this happens, one of the reasons is the "I'm not the owner" &lt;/p&gt;

&lt;p&gt;And this could mean many things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'm not the owner -&amp;gt; I'm not entitled to make any changes.&lt;/li&gt;
&lt;li&gt;I'm not the owner -&amp;gt; I have no idea how this works.&lt;/li&gt;
&lt;li&gt;I'm not the owner -&amp;gt; this is not my task.&lt;/li&gt;
&lt;li&gt;You are the owner, I expect you to do this work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what does ownership mean? All above assumptions can be true, so it is important to be clear on this. &lt;/p&gt;

&lt;p&gt;So I try to reply with: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'm the owner -&amp;gt; but you can raise a PR, and I will take a look at that.&lt;/li&gt;
&lt;li&gt;I'm the owner -&amp;gt; we can kick off something together, or provide some directions.&lt;/li&gt;
&lt;li&gt;I'm the owner -&amp;gt; but this is not our main priority.&lt;/li&gt;
&lt;li&gt;I'm the owner -&amp;gt; but we are both users of/involved in X.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I truly believe every library or service should have one, and only one, owner team in the organisation. My favourite description of ownership is:&lt;/p&gt;

&lt;p&gt;"The owner team should be the team having the holistic view on that project, having the last word on what gets in or not, and taking care of the general maintenance, and in many cases the ones on call if something goes wrong and needs quick attention"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8650u4ev9rs81mbrm221.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8650u4ev9rs81mbrm221.png" alt="I don't have time for it" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Friction point 2: I don't have time for it
&lt;/h3&gt;

&lt;p&gt;Some years ago I remember having a timesheet with: &lt;strong&gt;Code&lt;/strong&gt;, &lt;strong&gt;Test&lt;/strong&gt;, &lt;strong&gt;Review&lt;/strong&gt; and &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sure, because those are the only tasks, right? If I would need to break down my daily tasks, they would look more like: &lt;strong&gt;Read/Learn&lt;/strong&gt;, &lt;strong&gt;Code&lt;/strong&gt;, &lt;strong&gt;Debug&lt;/strong&gt;, &lt;strong&gt;Get into the Rabbit hole&lt;/strong&gt;, &lt;strong&gt;Code&lt;/strong&gt;, &lt;strong&gt;Test&lt;/strong&gt;, &lt;strong&gt;Review&lt;/strong&gt;, &lt;strong&gt;Go back 10 steps&lt;/strong&gt;, &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I think this is still alive in many orgs, where we have the conception that only writing code in "My repo" is part of my work. So many people only contribute privately and in "my personal time", we need to declare when changes are needed outside our repos and make contributions part of our daily work.&lt;/p&gt;

&lt;p&gt;After all we and our employer are benefiting from those contributions &lt;/p&gt;

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

&lt;h3&gt;
  
  
  Friction point 3: It is too hard
&lt;/h3&gt;

&lt;p&gt;Contributing can often leave us with a single feeling: &lt;strong&gt;This is too hard.&lt;/strong&gt; From figuring out how to set up the project and finding where to start, to understanding contribution directives and getting useful feedback. All of this can make contributing feel far more difficult than working in our own projects.&lt;/p&gt;

&lt;p&gt;To deal with this friction, awareness is key here. Encouraging people to contribute saying "it's easy, just push a PR" can end up with a frustrated contributor who rarely wants to come back. So personally I try to support people contributing together, and changing the "it's easy" to "it is challenging, but pay off" and "I'm able to help on that".&lt;/p&gt;

&lt;p&gt;On the other side creating a good path for contributors it also helps a lot, I love when projects provide progressive introduction to setup the project, being concise on the best way to raise things and whenever is possible also share how the reviewing process work so they know what to expect before hand. &lt;/p&gt;

&lt;h3&gt;
  
  
  In conclusion
&lt;/h3&gt;

&lt;p&gt;After sorting the friction point contribution brings a lot of benefits, for both maintainers and contributors. &lt;/p&gt;

&lt;p&gt;Breaking our own bubble makes us learn and be challenged in many different ways improving our knowledge.&lt;/p&gt;

&lt;p&gt;For maintainers it is most of the time better to get a feature to review rather that a feature request.&lt;/p&gt;

&lt;p&gt;Having people who are used to contributing internally and externally helps companies break down silos, reuse more software, and create a culture where people are eager to unblock themselves.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So...&lt;/em&gt; Let's go out there and contribute, help other to contribute and explicitly enable contribution!!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>innersource</category>
      <category>contribution</category>
    </item>
    <item>
      <title>El Peer Review no sirve para nada (Spanish)</title>
      <dc:creator>sarabadu</dc:creator>
      <pubDate>Thu, 24 Sep 2020 16:02:10 +0000</pubDate>
      <link>https://dev.to/sarabadu/el-peer-review-no-sirve-para-nada-spanish-kae</link>
      <guid>https://dev.to/sarabadu/el-peer-review-no-sirve-para-nada-spanish-kae</guid>
      <description>&lt;p&gt;Bueno, en realidad no creo que no sirva para nada, de hecho, el peer review me parece una de las herramientas más productivas para la interacción entre compañeros, el crecimiento de un equipo y la calidad de nuestro trabajo.&lt;/p&gt;

&lt;p&gt;Sin embargo, en muchos equipos de trabajo observe como menospreciaban esta práctica o bien la habían dejado completamente de lado. ¿Pero cuáles son los problemas?&lt;/p&gt;

&lt;h2&gt;
  
  
  No tenemos tiempo para eso
&lt;/h2&gt;

&lt;p&gt;Bueno esto lo escuche muchas veces tanto para los peer review como para los tests unitarios.&lt;br&gt;
Nunca vas a tener tiempo para las tareas que no les destines tiempo. &lt;/p&gt;

&lt;p&gt;Cuando el problema es el tiempo necesitaremos decisiones fuertes y de largo plazo. Tanto el peer review como el testing unitario o el TDD, son prácticas que demuestran resultados cuando son constantes, se mantienen en el tiempo y se hacen costumbre.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;¿Qué importancia tiene la calidad?&lt;/em&gt; &lt;br&gt;
&lt;em&gt;¿Qué tan valioso es la discusión entre colegas sobre el trabajo que estamos realizando?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;¿Cuánto tiempo sería aceptable dedicarles a estas prácticas?&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  ¿Y qué puedo hacer entonces?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Adapta el proceso (o crea un proceso) de desarrollo para que incluya el Peer Review y revisa que se cumpla.&lt;/li&gt;
&lt;li&gt;¿Necesitas convencer a alguien? Medí los tiempos de desarrollo y peer review&lt;/li&gt;
&lt;li&gt;Pon metas a largo plazo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y por último ten en cuenta que Arreglar cosas que no tuvieron peer review lleva tiempo también!!&lt;/p&gt;

&lt;h2&gt;
  
  
  No me gusta que me muestren mis errores
&lt;/h2&gt;

&lt;p&gt;Siempre puede existir algo de incomodidad a la hora de marcar y que nos marquen errores, pero tenemos que poner el foco en el objetivo por el cual hacemos estas practicas&lt;/p&gt;

&lt;p&gt;&lt;em&gt;El peer review es para que nosotros, como equipo, tengamos mejores resultados&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ese es el objetivo, &lt;strong&gt;¡si solo ese!&lt;/strong&gt; En el peer review se pueden encontrar errores, también para compartir puntos de vista, validar si lo que hicimos se entiende y aprender del otro. Si nos enfocamos en solo buscar y encontrar errores, estamos desperdiciando la posibilidad de crecer en otros aspectos.&lt;/p&gt;

&lt;p&gt;Por otro lado, acá juega mucho el cómo hacemos el peer review y como lo recibimos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuando hagas un Peer Review:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Intenta entender que decisiones tomo tu colega&lt;/li&gt;
&lt;li&gt;Toma nota de que cosas harías distinto y por que&lt;/li&gt;
&lt;li&gt;Intenta buscar todos los detalles posibles: mala identación, nombre de variables, ¡¡todo sirve!!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pero tu trabajo no termina ahí, si dejamos esto anotado y nos desentendemos estamos haciendo solo la mitad del trabajo...&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Júntate con quien realizo el desarrollo y revisen tus notas juntos&lt;/li&gt;
&lt;li&gt;Intenta reforzar "intentemos mejorar juntos con esto" para mantener el objetivo en mente&lt;/li&gt;
&lt;li&gt;Mide tus palabras, ten especial cuidado en cómo te comunicas frases como "acá te equivocaste", "esto está mal" son fácilmente reemplazable con "yo esto lo haría diferente", "esto se ve raro, revisemos", "no entendí esta parte"&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pero si algo está mal, está mal y yo le voy a decir porque un error es un error y etc....&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Si, si, pero ¿qué cuesta modificar un poco tus palabras para lograr el mismo objetivo? no hablo de decir que algo está bien cuando no lo es, ni que algo es aceptable cuando no lo es...  &lt;strong&gt;hablo de mostrar eso mismo de la manera mas amable que se te ocurra&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Y cuando recibas un Peer Review
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tu trabajo no está terminado, solo es un boceto que está a punto de perfeccionarse. Si tomas como una revisión de algo que ya estaba "terminado" puede ser chocante. Mientras desarrollas piensa "aún faltan los cambios del peer"&lt;/li&gt;
&lt;li&gt;Tu colega no es el enemigo, el enemigo es el llamado a las 3am por un bug, o el código spaghetti de hace 3 meses (que sí, vos escribiste)&lt;/li&gt;
&lt;li&gt;Da tu opinión no tomes el peer como una lista de cambios y ya, discute con el peer porque tomaste una decisión, porque elegiste ese nombre, porque te equivocaste ahí. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  El Peer Review puede impactar negativamente en mi carrera
&lt;/h2&gt;

&lt;p&gt;EL PEER REVIEW NO PUEDE SER MÉTRICA PARA MEDIR PERFORMANCE. -&lt;em&gt;necesito mayúsculas más grandes&lt;/em&gt; -&lt;/p&gt;

&lt;h3&gt;
  
  
  Bueno soy encargado de los Peer Reviews
&lt;/h3&gt;

&lt;p&gt;Si tu puesto es Jefe, PM, CEO, o simplemente el que le gusta seguir métricas para ver como trabaja la gente, te recomiendo lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No tomes los errores de un peer review como indicador de performance de una persona, solo lograras que nadie quiera encontrar errores, puede que al principio veas algún dato, pero con el tiempo no vas a tener información.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cuando planifiques prácticas de Peer Review, intenta plantearlas dentro del proceso de desarrollo, antes de "los últimos retoques" y antes de hacer las pruebas finales. si se planifican al final genera una sensación de retrabajo que no es necesaria&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pero encontré que Alejandra le encontró 500 errores a Anselmo, ¿Qué hago me quedo de brazos cruzados?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No, de ninguna manera, en ese caso felicita a Alejandra, está haciendo muy buen trabajo.&lt;br&gt;
Anselmo solo mostró un borrador de su trabajo, es esperable que se encuentren errores.&lt;/p&gt;

</description>
      <category>peerreview</category>
      <category>teamwork</category>
    </item>
    <item>
      <title>To:CEO; Subject:TESTING</title>
      <dc:creator>sarabadu</dc:creator>
      <pubDate>Mon, 31 Aug 2020 21:21:14 +0000</pubDate>
      <link>https://dev.to/sarabadu/to-ceo-subject-testing-4f17</link>
      <guid>https://dev.to/sarabadu/to-ceo-subject-testing-4f17</guid>
      <description>&lt;p&gt;Last week I received a mail from a user:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Subject:  &lt;strong&gt;"RE: Testing WTF"&lt;/strong&gt; &lt;br&gt;
Body:    &lt;em&gt;"please stop sending these test emails"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first, we tried to replace all the email addresses on the database and config files, but the feature that sends the emails uses a lot of business logic. So we wanted to test the feature with a more realistic and end-to-end approach.&lt;/p&gt;

&lt;p&gt;After looking for a while I found this lovely piece of software. &lt;a href="https://github.com/tweakers/MockMock" rel="noopener noreferrer"&gt;MockMock&lt;/a&gt; is a very simple but powerful SMTP mock server built in java.&lt;/p&gt;

&lt;p&gt;How it works:&lt;br&gt;
Simply download the MockMock.jar and run &lt;code&gt;java -jar MockMock.jar&lt;/code&gt; this starts the SMTP server on the port 25 and an HTTP server on port 8282.&lt;/p&gt;

&lt;p&gt;Now you can configure your SMTP server as &lt;code&gt;localhost&lt;/code&gt; on port &lt;code&gt;25&lt;/code&gt;, when the application sends an email just browse on &lt;code&gt;http://localhost:8282/&lt;/code&gt; and you'll see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdvp9arsdkcjuuhgptfv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdvp9arsdkcjuuhgptfv9.png" alt="mockmock email list" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we can see de detailed info of each mail:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3o2ye9m2arp3tqhh8agz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3o2ye9m2arp3tqhh8agz.png" alt="Alt Text" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also change the ports with &lt;code&gt;-p&lt;/code&gt; argument for SMPT and &lt;code&gt;-h&lt;/code&gt; for HTML like  &lt;code&gt;java -jar MockMock.jar -p 25250 -h 8080&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;I love this type of test because we can extend our test cases and look for the result in a fancy and safe way.&lt;/p&gt;

</description>
      <category>testing</category>
    </item>
    <item>
      <title>breaking my yotube bubble</title>
      <dc:creator>sarabadu</dc:creator>
      <pubDate>Sat, 22 Sep 2018 18:35:33 +0000</pubDate>
      <link>https://dev.to/sarabadu/breaking-my-yotube-bubble-5a6h</link>
      <guid>https://dev.to/sarabadu/breaking-my-yotube-bubble-5a6h</guid>
      <description>&lt;p&gt;i use YouTube for learning every day but i notice that my timeline only have mens channels, so i wonder where is the women in tech tutorials &lt;/p&gt;

&lt;p&gt;Every social app creates a bubble around us preferences and that is ok somtimes but after some time you are missing a lot of content&lt;/p&gt;

&lt;p&gt;Today i look at my YouTube home timeline and didnt see any woman &lt;/p&gt;

&lt;p&gt;I follow channels like fun fun function, devtips, kyle simpson talks, Coding train....&lt;br&gt;
Maybe YouTube in some point think uhmm let show mens talking about tech to think user.&lt;/p&gt;

&lt;p&gt;So id like to break this bubble whit women in tech channels.&lt;/p&gt;

&lt;p&gt;Can you recomend me some channels?&lt;br&gt;
Thanks!!!!&lt;/p&gt;

</description>
      <category>devdiscuss</category>
      <category>discuss</category>
    </item>
    <item>
      <title>who need unit test in their open source project?</title>
      <dc:creator>sarabadu</dc:creator>
      <pubDate>Mon, 27 Aug 2018 01:14:47 +0000</pubDate>
      <link>https://dev.to/sarabadu/who-need-unit-test-in-their-open-source-project-3l87</link>
      <guid>https://dev.to/sarabadu/who-need-unit-test-in-their-open-source-project-3l87</guid>
      <description>&lt;p&gt;i need to practice unit testing in java and js. And i want to start contributing to community&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>contrib</category>
      <category>newbie</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
