The problems with Swagger
If you don’t know Swagger, Swagger is a tool for documenting REST APIs. The documents are written in YAML, which I call XML for lazy people 😉
Before we continue I want to clarify that this post is based on personal experiences with Swagger. Some of the problems presented here are known Swagger’s limitations, some other are more related to how people use Swagger. However, I have observed that the usage is a common pattern, so although it is not a proper Swagger limitation, the tool tends to promote that kind of usage. The problems I am going to describe in this post happen in the context of documenting REST API using JSON and Java.
YAML is the new XSD
This is a Swagger misusage problem, not a problem from Swagger per sé. Nevertheless I have seen it in several projects. People think the YAML file is the new XSD, which means they treat the YAML file as a schema and just want it to generate the client model. However there are many problems that arise with this behavior.
Code generation and deserialization.
At first glance there is nothing wrong with code generation. It is actually seen as a productivity boost. Here is the thing though: When consuming a REST API you want to be as tolerant as possible. This means, you should only consider, or read, the elements in the payload that you need and ignore anything else you don’t. It also means, that you should not make any assumption regarding the structure of the payload. This is known as the Tolerant Reader Pattern . If you implement the tolerant reader pattern correctly, modifying the payload – i.e adding a new element – should not break the client code.
The problem of code generation with Swagger makes itself more evident with the enum type. In JSON there are no enums. That’s it. However you can define enums in Swagger. When I started using Swagger I thought the purpose of this was that you can see all possible values an enum has in the Swagger UI. However, when you generate the Java code out of the YAML, it generates the corresponding Java enums. In this way you end up with hard-coded values of what it was supposed to be a dynamic list. If the service provider adds a new element to the list, which means adding a new element to the enum, your code will break when deserializing the payload if you don’t re-generate the client model. So you see here how fragile your code becomes when it is generated. Adding a new element to the structure of the payload should NEVER break your code.
Again, this is not a Swagger problem, but a problem that arises from its usage. I think this behaviour comes from the SOAP/XSD world, where adding a new element to the payload meant a completely new version of the API.
To mitigate this problem just stop generating code and doing automatic deserialisation. Sadly, I have observed that’s a very hard habit to break.
Nobody reads the documentation anymore.
In one particular project we have experienced a lot of misunderstandings when a team tries to consume an API by blindly generating the client code and not reading what the APIs does or how it behaves or , worse, completely ignoring how REST works. The only interesting bits are the structure of the model, the URL and the HTTP method. These are just API details. The true value of an API is what it does, what it enables, but nobody seems to care.
No, this is not a swagger problem. However, I have experienced that many people just want to use swagger to quickly generate code and implement the client’s API. Swagger is like an enabler of this behaviour.
No Hypermedia support
Swagger does not support Hypermedia. It has been considered but as of now Hypermedia is still not supported. When I mention the lack of Hypermedia support in my projects the normal reaction is more or less a “so what?”. Well, as Roy Fielding says
Hypermedia is a constraint. As in, you either do it or you aren’t doing REST. You can’t have evolvability if clients have their controls baked into their design at deployment. Controls have to be learned on the fly. That’s what hypermedia enables.
In my opinion you want to have hypermedia just because of evolvability alone. The fact that using hypermedia means you are really doing REST is a nice secondary effect. Let me explain that a little bit further.
Suppose you have the following resource being called by a GET request.
1 |
GET http://myapi.com/orders/123 |
The service will return the payload plus a set of links.
1 2 3 4 5 6 7 |
"links": [ { "href": "http://myapi.com/orders/123", "rel": "cancel", "type" : "DELETE" } ] |
That may look simple but it is actually quite powerful because:
1. Links are dynamic and the service provider decides when to send those links. The client just reacts to them – i.e. it may show a button to cancel the order.
2. what the URL looks like is irrelevant.
How do the above points contribute to evolvability?
In the above example, the business logic that decides whether an order may be canceled or not resides on the server, not on the client. Which means, you can change that business logic whenever you want without having to change the clients.
Since the links are dynamically provided by the server, they may be added or changed at any given point in time without having to change the clients as the URLs are not hard-coded.
I hope at this point I was able to explain that you can change the business logic of your API and even how a URL looks like without modifying the clients that use your APIs and that’s the key aspect of evolvability: The possibility to adapt your API without having to change (compile and redeploy) the clients.
Without hypermedia, the clients would probably have to parse the payload to see if there are some kind of status and then evaluate that status to take a decision whether the order may or not be canceled. It might seem harmless but that usually leads to distributed business logic baked in every consumer.
So…
if you have evolvable APIs and clients that implement the tolerant reader pattern, you never need to version your APIs!
Swagger is URI Centric
As you may be aware now, the URI is a detail of the API and most of them are dynamically created by the service provider via Hypermedia. Swagger focueses heavily on the URIs. Take a look at the following image:
You see a list of all possible URIs an API has whereas the only URI that should be of your interest is the entry URI of your API. The rest of the URIs are provided by Hypermedia.
YAML generation.
If you are doing API first, there are several frameworks to generate YAML for Swagger. In Java many of these frameworks rely heavily on annotations. The official swagger library works that way. Let’s take a look at it:
1 2 3 4 5 6 |
@Path("/{username}") @ApiOperation(value = "Updated user", notes = "This can only be done by the logged in user.") public Response updateUser( @ApiParam(value = "name that need to be updated", required = true) @PathParam("username") String username, @ApiParam(value = "Updated user object", required = true) User user) {...} |
You can see that, apart from the fact that swagger repeats itself a lot with redundant annotations, it also leads to having more annotations than actual code. You also have to be aware that, normally, an API description is more complex than This operation updates a user. In our current project we even have complex tables describing use cases. Try to write that inside an annotation.
Fortunately there are other frameworks that generate YAML from the Javadoc. If you have to use Swagger, I strongly recommend using one of those frameworks.
Regardless of the framework you use, annotation or Javadoc based, you will probably leak implementation details in your documentation. When generating the YAML, those frameworks use the name of the class you use as REST DTO to generate the model definition. So, if you use something like UserResponseBody, this name will be later baked in every client that uses code generation. If you later decide to rename your class, you will cause a lot compile errors in those clients. I have seen that happen and I think that is an absurd consequence of generating documentation.
If you happen to do contract first, I can tell you it is not a pleasant experience to write the YAML yourself for non-trivial APIs even with the swagger editor.
Swagger makes a very good first impression
Yes, that is a problem. When people start evaluating tools for documenting REST APIs, they eventually find Swagger, give it a try and are amazed by how easy and fast you can get your first documentation up and running. Besides, many people think that the possibility to generate code out of the documentation is a good idea and Swagger provides that as well.
So the fact that swagger makes a great first impression, makes its limitations and constraints less apparent. Personally, I don’t like a tool that keeps me from using an essential and powerful feature like Hypermedia. I don’t like this kind of constraint to be imposed by a tool. You may use hypermedia or not, but don’t let the documentation tool take that decision for you.
I think the fact that Swagger makes a good first impression and that people see Swagger as a kind of a REST schema, has made Swagger very popular.
What are the alternatives?
The original problem swagger tries to solve is: API documentation. So, if you need to document an API, use a format that is created for that purpose. I highly recommend using Asciidoctor for writing documentation. There are some tools for Java that help you with this. However they are somehow technology specific. For instance Spring REST Docs works very well if you are using Spring / Spring Boot. If you use JAX-RS, the JAX-RS Analyzer might be a good option.
Conclusion
As I mentioned at the beginning of this post, some of the Swagger’s drawbacks are actually a usage problem. However, Swagger imposes some constraints, like the lack of hypermedia, which I personally think are show stoppers. I’m not saying you should immediately stop using swagger or never use it, but I hope you are now aware of its constraints and limitations. I hope you now understand that, if you are using swagger, you are probably giving up one of the most powerful feature of RESTful APIs. You are giving up evolvability!
Comment article
Recent posts






Comments
Andrew
[…] you can define enums in Swagger. … If the service provider adds a new element to the list, which means adding a new element to the enum, your code will break when deserializing the payload if you don’t re-generate the client model. So you see here how fragile your code becomes when it is generated. Adding a new element to the structure of the payload should NEVER break your code.
This is an extreme view of the world. Imagine we’re talking about a banking API. The transactions types are enumerated, e.g. Deposit, Withdrawal, etc.
Now a new transaction type is added. Do we really expect a piece of code that switches on transaction type to keep working? Hardly. Almost certainly it will break, with something like “unknown transaction type”.
Adding a new value to an enum is quite different from adding a new field to a JSON object. The latter one is probably what we mean by “adding a new element to the structure of the payload should NEVER break your code.”.
Eduardo Spaki
I see swagger more like a tool (a test tool) than a final documentation page.
Carlos Barragan
Hi Javier,
I haven’t used RAML extensively so I cannot give you an informed opinion about it. However, RAML looks more like an “API Platform” where documenting an API is just one of the features.
Thanks for your comment!
Nick
API Blueprint, and it’s supporting ecosystem of tools were a good choice for our team. That whole effort was (is?) driven by the people at Apiary.io, which was very recently acquired by Oracle.
Also, complaining about people only using Swagger to generate client code isn’t really valid. You cannot blame a documentation tool for people sucking. Sometimes, people just suck. Developers, as a cohort of people, contain some members who suck. Period.
Javier Antoniucci
Hi Carlos! Good article! What are your thoughts about RAML and API first design?
Carlos Barragan
Hello Vojtěch,
the thing is, I don’t want to generate client code because that couples your client and the server and it is difficult to evolve the API afterwards.
Thanks for your comment!
Carlos Barragan
Hi Ezequiel,
the main issue here is that I don’t want to generate client code. In my opinion that’s a tricky approach because it couples your server and your clients and that is what I’m trying to avoid.
And yes, not all clients need access to the whole API and that’s why code generation is the wrong approach in my opinion because it is an “all or nothing” approach. A client should just use the APIs it needs.
Thanks for your comment!
Carlos Barragan
Hello Michael,
Maybe a little bit more context can help to explain my concerns.
If your customer is a big company, they usually want to use a certain set of tools for their system and standardize them for every department. In this context, I think it is important to do extensive evaluation of the tools because it is quite difficult to change them afterwards. Unlike a big customer, if you are working on a small project within a small / medium company, maybe changing the toolchain is not a big problem if, along the way, you realise you need other tools.
In my case I’m working with a big company where it is quite difficult and bureaucratic to change something and my concern is, they chose a tool that puts some limitations from the very beginning.
And yes, you may not always need hypermedia and Swagger is a good tool for documenting those kind of RPC over HTTP APIs.
RAML and API Blueprint are very similar to Swagger in my opinion and they were also evaluated in my current project. I mentioned Asciidoc as a true alternative, because it is a completely different approach.
You are right. Our use case requires a tool that supports RESTful APis and Swagger is not that tool.
Thanks for your comment!
Carlos Barragan
Hi Edhem,
With hypermedia you can avoid implementing business logic on the client side. The clients should interpret the links provided by the server. The links are like another level of indirection. Hypermedia actually enables writing smart clients.
Regards.
Ezequiel Gonzalez Rial
Hi Carlos,
First of all, you are using a generation tool to create a client app. The skeleton is based for a general use case and probably you can add hypermedia to the client’s template if you really needed. But the issue is which hypermedia?? HAL, JSONAPI, JSON-LD, Collection+JSON, SIREN … probably the one you like the most (none of them have become the standard option)
So it’s really your focus and you can contribute back you can create a more interesting client generation tool and support any of the hypermedia types and in the best case scenario, all of them.
About using a code reflection tool, I’m sorry but it might have adverse problems. If you want a complete documentation (user/technical) you need someone that can “actually” write a complete and compelling text. That means that probably not all the developers are the best option to do it. Also, a good documentation means that a 200 line file, will require 500 comment lines (I did it, never again)
So how to use the tool? In my experience, in the SERVER as it is: an specification. That means to can use it as part of your framework and input it as a middleware so it can take care of routing and type verification, at least for the input. And in the CLIENT, it will depend … not all the clients need full access to all the methods, so you can use it as a baseline, a first blueprint for the code generation and add or remove the parts that you don’t need. But in the long run, client code will mutate in ways that you will not expect, so you need to maintain it as independent from the specification as possible.
My two cents
Michael
In my own experiences, a lot of Swagger implementations receive little attention in small-scale, or internal systems. It’s relied on to be a form of interface to what is otherwise web-only, and as a result, some of the health endpoints are often left exposed, because it’s just used out-of-the-box. I’ve seen many cases where production platforms have shutdown methods exposed, and other damaging functionality done – simply because Swagger became a lazy way to make API calls known.
Shai Sachs
Yeah I’m with Michael on this one. Sounds like you’re having a tooling problem first and foremost.
I would also encourage you to check out API Platform (http://api-platform.com/) – I know it’s not Java, but it does some pretty amazing things, like generating hypermedia (in three different formats!) for free – right alongside Swagger docs. The codegen is pretty nice there too.
Michael Gorianskyi
Swagger (OpenAPI) is a specification first of all and a set of tools around it, that allow you to do a lot of neat things. The documentation generation is only one of them.
Lack of hypermedia might be a show stopper for you, but not everybody in the world absolutely needs hypermedia neither you can force clients to use it properly. Here’s a nice article on this point: http://www.ben-morris.com/pragmatic-rest-apis-without-hypermedia-and-hateoas/.
The correct alternatives to Swagger should be RAML and API Blueprint (not a text processor).
It’s not problem with the tool, it’s your use-case requiring a different one.
Stephan Westen
You coin the term “evolvability” – as a suggestion you might want to refer to the open/closed principle?
https://en.wikipedia.org/wiki/Open/closed_principle
Vojtěch Habarta
For generating client code I would like to recommend https://github.com/vojtechhabarta/typescript-generator. It generates TypeScript declarations from Java (JSON data classes, JAX-RS service).
We use it in system where REST service has just one client (UI). There we don’t need hypermedia. With typescript-generator we have better refactoring experience because many incompatible changes are caught in build time. Business logic is only on server, we send “canUpdate”, “canDelete” etc. flags to the client.
BTW typescript-generator can also generate JSDoc comments from Javadoc comments and recently also from Swagger annotations.
Pieter
You might want to take a look at Spring REST Docs for a tool that makes a different set of tradeoffs, that it sounds like you might prefer, from reading this post.
https://projects.spring.io/spring-restdocs/
Wojciech Langiewicz
I have been using JSON API for one project with great success: http://jsonapi.org/ (this is somehow alternative to swagger, but it has also a lot of features that swagger doesn’t have) – Definitely recommend to test it out for the projects starting from scratch, it follows Hypermedia format to large extend so it would be very hard to introduce to already existing project that doesn’t follow REST or Hypermedia
Edhem
Hi,
I totally understand the benefit of hypermedia but if you need to build a fat client, because of a limited connection to the api server, you are forced to implement some business logic on client side. Actually, this is still big problem for companies with a large number of client devices in rural area. From my perspective hypermedia is a limitation to build self-aware and smart clients and thats a reason why companies avoiding hypermedia.