<?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: Andrew Tatomyr</title>
    <description>The latest articles on DEV Community by Andrew Tatomyr (@tatomyr).</description>
    <link>https://dev.to/tatomyr</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F270513%2F41cb1e8b-ed75-4039-9a51-93e69dc94908.jpg</url>
      <title>DEV Community: Andrew Tatomyr</title>
      <link>https://dev.to/tatomyr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tatomyr"/>
    <language>en</language>
    <item>
      <title>Are you migrating from Spectral? Redocly just got something to help you with that!</title>
      <dc:creator>Andrew Tatomyr</dc:creator>
      <pubDate>Tue, 13 Oct 2026 11:26:43 +0000</pubDate>
      <link>https://dev.to/tatomyr/are-you-migrating-from-spectral-redocly-just-got-something-to-help-you-with-that-2gld</link>
      <guid>https://dev.to/tatomyr/are-you-migrating-from-spectral-redocly-just-got-something-to-help-you-with-that-2gld</guid>
      <description>&lt;p&gt;&lt;strong&gt;Spectral&lt;/strong&gt; has been the default OpenAPI linter for years, and it did serve the community well. It made linting API descriptions in CI a normal thing, ensuring quality and consistency across your API documentation. Unfortunately, the recent state of its maintenance has raised security and reliability concerns to the extent that some are forking the project to continue development independently. In August 2026, &lt;strong&gt;SmartBear&lt;/strong&gt; even had to explain the reasons in this &lt;a href="https://blog.stoplight.io/an-update-on-spectral-from-smartbear" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; that describes how the July 14 supply chain attack reached &lt;strong&gt;Spectral&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, there's a better option than forking &lt;strong&gt;Spectral&lt;/strong&gt;: migrating your linter to &lt;strong&gt;Redocly CLI&lt;/strong&gt;. It was built in the same spirit, but on a different architecture, and it is actively maintained. Let me explain why the architecture matters.&lt;/p&gt;

&lt;p&gt;A typical &lt;strong&gt;Spectral&lt;/strong&gt; rule contains the &lt;code&gt;given&lt;/code&gt; section, which is intended to describe the target. It usually looks like a cryptic enchantment of JSONPath expressions like this: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;$..[?(@ &amp;amp;&amp;amp; @.type=="array")]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(and it's not even the hardest one). Can you guess what this means without asking an AI tool? I cannot. As you can imagine, writing and maintaining such rules can be quite challenging and error-prone. This is not a glitch but an architectural flaw: JSONPath matches patterns in a document tree and knows nothing about the specification.&lt;/p&gt;

&lt;p&gt;It affects even the built-in rules. The expression above is the target of &lt;code&gt;array-items&lt;/code&gt; from the &lt;code&gt;spectral:oas&lt;/code&gt; ruleset, which requires every array schema to define its &lt;code&gt;items&lt;/code&gt; type. However, it can report false positives where the same properties appear in a different context. Here is a valid OpenAPI description with an example payload:&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;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.1.2&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Widgets&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/widgets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A form field&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;example&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;array&lt;/span&gt; &lt;span class="c1"&gt;# just data, not a schema&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Spectral&lt;/strong&gt; 6.16.3 reports an error on the last line: the example payload is not a schema, but JSONPath cannot tell the difference. And this is not the only case of possible false positives.&lt;/p&gt;

&lt;p&gt;Instead of matching paths, &lt;strong&gt;Redocly CLI&lt;/strong&gt; walks a type tree defined by the specification, so a &lt;code&gt;Schema&lt;/code&gt; is always a schema, and &lt;code&gt;$ref&lt;/code&gt;s are followed into other files. The equivalent rule from the &lt;a href="https://github.com/Redocly/redocly-cli/tree/main/cookbook/configurable-rules/required-items-for-array-schemas" rel="noopener noreferrer"&gt;Redocly cookbook&lt;/a&gt; is a few lines of YAML that say exactly that: subject &lt;code&gt;Schema&lt;/code&gt;, where &lt;code&gt;type&lt;/code&gt; is &lt;code&gt;array&lt;/code&gt;, require &lt;code&gt;items&lt;/code&gt;. It passes the description above and still reports a real &lt;code&gt;type: array&lt;/code&gt; schema without &lt;code&gt;items&lt;/code&gt;. This makes rules easier to understand, more precise, and less error-prone.&lt;/p&gt;

&lt;p&gt;It's easy to add and maintain new API specifications too, and this is one of the reasons &lt;strong&gt;Redocly CLI&lt;/strong&gt; supports the widest range of API standards out of the box.&lt;/p&gt;

&lt;p&gt;The performance is also significantly better, which makes &lt;strong&gt;Redocly&lt;/strong&gt; much more suitable for large-scale API projects, especially when you are running your checks in CI/CD pipelines (see &lt;a href="https://dev.to/albinator/openapi-linters-which-one-is-the-best-in-a-real-test-1n2j"&gt;OpenAPI linters: which one is the best in a real test?&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;And if you've been into API documentation for some time, you probably already know that &lt;strong&gt;Redocly CLI&lt;/strong&gt; has always been the best tool for multi-file setups, which are far easier to maintain than one huge file. So, chances are you already have it installed in your project for bundling API documents prior to other steps. But it also lints the whole API description from the entry point and reports problems in the file where they are, so you don't have to bundle your description before you can check it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration
&lt;/h2&gt;

&lt;p&gt;If the above sounds convincing, and you've decided to change the linter, it might be an afternoon task if you're using a default ruleset. Check out the official &lt;a href="https://redocly.com/docs/cli/guides/migrate-from-spectral" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; first if this is the case. However, if you have lots of custom rules, migrating them might be challenging.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, reading and understanding the existing &lt;strong&gt;Spectral&lt;/strong&gt; rules is an art form of its own. On the other hand, the &lt;strong&gt;Redocly&lt;/strong&gt; rules system offers you three levels of complexity: built-in, configurable, and plugin rules. So you might ask yourself which type is right for a specific rule. If there's a built-in rule that fits your needs, it's usually the easiest and most efficient choice. If not, &lt;a href="https://redocly.com/docs/cli/rules/configurable-rules" rel="noopener noreferrer"&gt;configurable rules&lt;/a&gt; allow you to define custom criteria without writing any code -- just declarative YAML in your config file. And for the most complex scenarios, &lt;a href="https://redocly.com/docs/cli/custom-plugins/custom-rules" rel="noopener noreferrer"&gt;plugin rules&lt;/a&gt; -- custom code in a plugin -- give you the flexibility to implement any specialized logic you require.&lt;/p&gt;

&lt;p&gt;Sounds complicated? It might be at first glance, but it's actually easier than you might think, especially with the new tools &lt;strong&gt;Redocly&lt;/strong&gt; just dropped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Skill
&lt;/h2&gt;

&lt;p&gt;If you're using an AI agent, you can leverage &lt;strong&gt;Redocly&lt;/strong&gt; skills, in particular &lt;code&gt;redocly-lint-rules&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add https://redocly.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've built it with efficiency in mind, grounded in metrics. And it works even better with the new &lt;strong&gt;Redocly CLI&lt;/strong&gt; &lt;a href="https://redocly.com/docs/cli/commands/inspect-node-types" rel="noopener noreferrer"&gt;&lt;code&gt;inspect-node-types&lt;/code&gt;&lt;/a&gt; command, which helps you (or your LLM) explore the node types of your API description in detail, so the rule targets the right node from the start.&lt;/p&gt;

&lt;p&gt;We ran sample migrations using different &lt;strong&gt;Claude&lt;/strong&gt; models for the sake of comparison, with and without the skill and the &lt;code&gt;inspect-node-types&lt;/code&gt; command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;small&gt;Update: While I was working on this article, the repository I took as a migration sample (it's the &lt;a href="https://github.com/Azure/azure-api-style-guide" rel="noopener noreferrer"&gt;Azure API Style Guide&lt;/a&gt;) was archived precisely because of a migration from &lt;strong&gt;Spectral&lt;/strong&gt;. They actually decided to go with &lt;strong&gt;TypeSpec&lt;/strong&gt;, but it's curious that they were also considering migration around the same time.&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The table lists the percentage of the rules that were migrated correctly.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Bare LLM&lt;/th&gt;
&lt;th&gt;Skill alone&lt;/th&gt;
&lt;th&gt;Skill and Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Haiku 4.5&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;td&gt;27%&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sonnet 5&lt;/td&gt;
&lt;td&gt;75%&lt;/td&gt;
&lt;td&gt;82%&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5&lt;/td&gt;
&lt;td&gt;93%&lt;/td&gt;
&lt;td&gt;97%&lt;/td&gt;
&lt;td&gt;98%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From the figures, it's clear that &lt;strong&gt;Haiku&lt;/strong&gt; was not capable of migrating the rules whether used with the skill or not. &lt;strong&gt;Sonnet&lt;/strong&gt; performed a lot better, especially with the skill and the command, exploring the node types instead of guessing and retrying, but still required manual intervention in 1 of 5 cases.&lt;/p&gt;

&lt;p&gt;The most interesting case was &lt;strong&gt;Opus&lt;/strong&gt;, which showed 93% correctness even as a bare LLM. However, the quality of the rules written matters as much as the count. Built-in and configurable rules are pure YAML, whereas plugins are custom JavaScript you have to write and maintain. The chart breaks the correct rules down by type.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiaj9h3p3ynpkk8282ftk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiaj9h3p3ynpkk8282ftk.png" alt="AI models performance breakdown" width="800" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without the skill, the model wrote three-quarters of the correct rules as plugins. With the skill and the command, that share dropped to a quarter, so you have far less custom code to maintain.&lt;br&gt;
Also, the skill actually improved around 10% of the source rules while migrating them: the resulting rules catch cases the original rules missed. This is no surprise, given how hard it is to comprehend the &lt;strong&gt;Spectral&lt;/strong&gt; targeting, even for the rule authors.&lt;/p&gt;

&lt;p&gt;Based on the impact, we baked the &lt;code&gt;inspect-node-types&lt;/code&gt; command into the new skill so you don't have to worry about it at all -- an agent will call it when needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;small&gt;Update: While I was finishing this article, &lt;strong&gt;Anthropic&lt;/strong&gt; released &lt;strong&gt;Opus 5.5&lt;/strong&gt; and &lt;strong&gt;Sonnet 5.5&lt;/strong&gt;, so I re-ran the same migration with them. I didn't go through the results in the same detail, but at first glance &lt;strong&gt;Opus 5.5&lt;/strong&gt; performed about the same as &lt;strong&gt;Opus 5&lt;/strong&gt;, while &lt;strong&gt;Sonnet 5.5&lt;/strong&gt; did much better and got close to &lt;strong&gt;Opus&lt;/strong&gt;. The skill had the same effect as before: fewer plugins, more configurable rules.&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Visual Builder
&lt;/h2&gt;

&lt;p&gt;Still, there might always be some corner cases that require manual intervention to ensure the rules are correctly constructed. So, if you want to tune the rules, or you just want to create one manually, &lt;strong&gt;Redocly&lt;/strong&gt; just shipped a &lt;a href="https://redocly.com/config-rules-builder" rel="noopener noreferrer"&gt;visual builder&lt;/a&gt; for configurable rules.&lt;/p&gt;

&lt;p&gt;It helps you construct a rule by playing with your specific API description, so you can instantly see which nodes it matches. It is kind of similar to using &lt;strong&gt;Redocly&lt;/strong&gt;'s VS Code extension, where you can inspect the targets simply by hovering over them in the editor, but it lets you write a rule without even touching the keyboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The new &lt;code&gt;redocly-lint-rules&lt;/code&gt; skill and visual builder are intended not only for migration. More importantly, they can help you write better rules on top of &lt;strong&gt;Redocly&lt;/strong&gt;'s default presets, which is the intended way to use a linter -- your API style guide should reflect your team's needs. The quality of rules matters: the skill ensures not only their correctness but also that the setup is optimized for maintenance. And if you are migrating from another linter, chances are that you can even improve the existing rules along the way.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I'm a Redocly CLI maintainer&lt;/em&gt;&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>tooling</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
