Fixing Spectral’s Biggest Problems

Aidan Cunniffe 2023-11-09

Every week we meet API teams struggling to govern the hundreds of APIs their businesses are built upon. As the size of companies and their API ecosystems grow, it becomes impossible for the small team of API experts to be everywhere at once — and even if they could, nobody wants to be a gatekeeper.

Automated API linters (like Spectral) test each OpenAPI spec and makes sure it follows the style guide. This makes it easier for developers to build the right API, the first time, and helps make the manual API reviews more focused and helpful. In theory, great. In practice, anyone who has tried to adopt traditional API linters at scale runs into the same problems:

1. Lifecycle-aware Rules

You just wrote V1 of your team’s automated API style guide and now you’re ready to turn these 100 rules on across your company. What happens next?

Imagine you have a rule that enforces Param-Case names for query parameters. If you thought to add a rule like this, it is probably because many of your APIs don’t use Param-Case today. Now teams are going to either have to “fix” the existing APIs by introducing breaking changes or ignore this and countless other errors. You have a style guide, but because of the noise everyone has already stopped paying attention to it.

Linting is the right abstraction for code because we can fix all the lint warnings in a code base without breaking its consumers. But if you go fix all the failing rules in an API, it creates a mountain of breaking changes, sure to halt all progress internally and upset your external consumers.

What you really meant to do when you added that Param-Case rule was to say all new query parameters should use Param-Case. Existing endpoints don’t need to follow this convention and shouldn’t emit lint errors, but new endpoints should. We want to get a little better every single day, not rewrite everything all at once.

Instead of running your rules on the entire API spec like Spectral, Optic runs your rules on the diff between two spec versions. It understands API changes and where each endpoint is in its lifecycle so it can run different Spectral rulesets depending for new API endpoints and legacy ones:

spectral:
  # these rules run on new endpoints
  added:
   - pagination.spectral.yml
   - naming.spectral.yml
   - paths-and-resources.spectral.yml
  # these rules run on new + existing endpoints
  always:
   - security.spectral.yml
   - documentation.spectral.yml
   - gateway-extensions.spectral.yml

Now you can turn on an API Style Guide, align your teams, and ship better APIs without causing havoc. It’s simple, but it has such a large impact.

2. Rules about versioning + breaking changes

Since Optic runs your API Style Guide on the diff between versions of your API, it’s able to catch breaking changes and enforce your versioning policies. Breaking Changes are impossible to catch when you are only looking at one version of the specification at a time (as Spectral and other linters do).

We have built-in breaking change rules that you can enable here. You can also write custom breaking changes rules in the Optic SDK or using LintGPT.

alt

3. Write Rules in Natural Language

It is much easier to talk about your API Standards than to write Spectral rules that enforce them. When we meet new teams, they often have all their standards written down in a PDF or on Confluence and have only managed to write Spectral rules for a small fraction of them. The rules they have written each require hours to write, test and maintain.

In Spectral you would write all this YAML:

  az-path-case-convention:
    description: Static path segments should be kebab-case.
    message: Static path segments should be kebab-case.
    severity: info
    formats: ['oas2', 'oas3']
    given: $.paths.*~
    then:
      function: pattern
      functionOptions:
        # Check each path segment individually and ignore param segments
        # Note: the ':' is only allowed in the final path segment
        match: '^(\/([a-z][a-z0-9-]+|{[^}]+}))*\/([a-z][a-z0-9-]+|{[^}]*})?(:[A-Za-z0-9]+)?$'

With Optic you just have to say what the rule is in english or your native language: Static path segments should be kebab-case.. It is literally 1000 times faster.

Optic’s LintGPT tool uses the latest advanced in AI to let you write your API Standards in natural language. You can now go directly from your list of API Standards to a governance tool that can check them without having to code the rules. LintGPT has already saved teams 100s of hours.

Over the last 2 weeks I've asked teams which rules have been difficult to write rules for using Spectral and LintGPT was able to work with them immediately. Even I was impressed at how much it could check that isn't possible to check for with code.

Example Rules:

  • 2xx response codes must not return anything that looks like an error
  • Responses that return an array at their root MUST wrap that array in an object. ie {'users': []}
  • Properties that sounds like they are dates should use format: datetime or format: timestamp
  • Properties that sounds like a boolean should be type:boolean
  • When a property is named 'id', it should be more be made more specific: ie user_id, not just id
  • Number properties should define a default and minimum
  • A number properties minimum can not be decreased

Optic helps your team get the promised benefits of linting, at enterprise scale. Get Started today!

Want to ship a better API?

Optic makes it easy to publish accurate API docs, avoid breaking changes, and improve the design of your APIs.

Try it for free