Why I avoid SDKs in production
brandur.org
brandur.org
We built that SDK so that Junior Dev Jimmy can make calls to our API and actually finish integrating our service without giving up. That guy can't code his way out of a paper sack, and certainly couldn't successfully send us an HTTP request or parse the result that we sent back. We built him a client library so that he'll stop sending us stupid emails asking how to call our server-side API using jQuery or, not making this up as it's happened several times from several customers, a form on an HTML page.
Anything we can do to get that guy up and running and done implementing our API, we'll do. Because the guy who hired him is paying us money, and the last thing we want to do is explain why he needs to fire his only developer and find a new one.
Have you considered that maybe an API should be callable using `jQuery.ajax(...)` or a simple `application/x-www-form-urlencoded` POST?
(...or was this satire?)
I'm referring to API calls that you really really only want to ever do from your server. Things like starting and stopping EC2 instances, where you need to send along your API keys that are essentially the keys to your bank account and your business's future survival.
Now imagine, as the owner of that service, that your customer is sending those keys in plain text to the client, and wondering why he's having a hard time starting EC2 instances using javascript from his user's browser.
I have trouble thinking of any API that shouldn't be callable from a client.
Clearly the keys need to be obtained somehow, but that was out of band for my application. Once they are obtained I don't see any reason why passing them from a 3rd party app over HTTPS (using CORS) is any less secure than using session based authentication with a 1st party app.
In both cases you are vulnerable to MTM and XSS attacks. Sessions expire which is nice, but that's the only real difference.
I certainly don't want to dissuade companies from writing SDKs. They're great products and really help in getting people much needed traction as quickly as possible. For the GA of the new Heroku API we'll be shipping SDKs in as many languages as we can support!
If his solution works, and is widely adopted, then he will revisit and add all those things that the OP mentions, but right now he is just trying to get something working.
An SDK is great for that.
When I build an app that needs to consume some REST API, if the REST API provider doesn't already provide an SDK, I'll create my own in each programming language that is useful to me (Objective-C to build an iOS app; Java to build an Android app; etc). To me its the logical thing to do, especially if I intend to create more than 1 app that consumes the REST API in the same language (iOS POC app 1, iOS POC app2, ...).
However, creating your own SDK is pretty much what the OP suggests--it gives him the level of control that he needs.
Why can't we all just get along?
I think it's great for someone to say:
"Hey guys, I find it fits in my workflow really well to not use SDK's, and I
feel I get a lot of benefit from it."
I think we'd all agree that a similarly toned response would be great: Interesting approach, thanks for sharing why you do things that way. If I
may chime in as someone who designs these SDK's, we really target them
towards people who have less experience, and who need simpler tools to get
things done. We'd counsel everyone to keep this in mind when picking tools
to use for their projects. Remember, always be thorough in exploring your
options and choose the best tool for the job!
See how nice that could have been, for both parties? I don't think we benefit from titles that come across as vaguely hostile like "Why I Don't Want Your SDK in Production", or from responses that might be interpreted as overly dismissive like "That's cool. You're not the guy we built it for".I am conscious that this post might come across as condescending or insulting, but I'm genuinely not trying to go for that. I like hearing the different opinions people have, and it bothers me that there's such low level hostility and tension in much of the discussion I see. I hope we can all be a little nicer when we talk online.
Seriously, we can all get along!
I'll admit that my original title was somewhat linkbait-y and fully respect the moderator's decision to amend it. That said, while the title was designed to incite discussion (and there is disagreement in a good discussion), I hope that the article's actual content conveys the intended respectful tone. Let me know if it doesn't.
APIs which don't also maintain SDKs - in my experience - don't understand the cost of making breaking changes, and aren't as 'nice' to consume. When the SDK is built, these issues are discovered: I appreciate that it's not me that has to go through those pains.
I think SDKs are great. The intent of the article was to document why I will sometimes opt out of using them.
case class HttpCall[OutputType](
verb: String,
url: String,
queryParams: List[(String,String)],
formParams: List[(String,String)],
resultType: Class[OutputType])
trait Api {
def getMyTasks(): HttpCall[List[Task]]
def getTask(taskId: String): HttpCall[Task]
}
The SDK can also provide a driver for making the http calls: trait Driver {
def call[Out](httpCall: HttpCall[Out]): Out
}
So now api invocations look like this: val myTasks: List[Task] = driver.call(api.getMyTasks())
val aTask: Task = driver.call(api.getTask("foo"))
What's nice about this design is that the consumer can write their own driver if they don't like yours. Prefer asynchronous over synchronous? No problem! trait Driver {
def call[Out](httpCall: HttpCall[Out]): Future[Out]
}
Everything in the article except for grep-ability can be achieved with this kind of design.I can definitely see this as a workable solution. The only thought I have is that once in a while behavior like retry/idempotency support might have to be tweaked on a per-endpoint basis rather than from the general `call` method, and at that point things get a little less pretty.
In general I'd say that the ideas expressed in the article apply more to dynamic languages where you see much more of the convention of SDKs just giving you back hashes instead of strongly-typed models representing foreign resources. In something like Scala, the barrier of having to implement every model yourself might be an extra motivator for just defaulting to the SDK.
Dual-column layouts that still require me to scroll to the bottom of the page, and then back up, and then back down.
The multi-column layout will only trigger at large screen sizes. The idea here was to fill some of that extraneous white space you get when browsing full screen on big monitors. I may revert it.
1 | 4
2 | 5
3 | 6
try 1 | 2
-----
3 | 4
-----
5 | 6
...kinda like reading both sides of a book spine.I naturally worry a bit that the reader's eye will have a hard time tracking which segment it should be looking at next (say they accidentally jump from 1 --> 3 for example). But like you said, if you used the horizontal rule between say 1/2 and 3/4, and just had whitespace between 1/2, it might be distinctive enough for a fluid reading.
Thanks for the tip. I'll draw up a prototype.
As with everything, the issue is twofold. In the case of Heroku and your user-base it is reasonable to expect your users are closer-to-wire than an iOS guy hacking in Cocoa Touch.
From your point I can't agree more. A well designed HTTP APIs goes a long way here. Putting a Cocoa Touch developer's hat on and I can't be bothered learning HTTP, checking all the possible responses, handling redirects and errors when the only thing I need is to just fire a single Mixpanel track event...
Sort of C/C++ vs. let's say a Ruby. Assembler anyone? Stay on the metal or develop (and crash:) faster with some trade-offs.
Anyways, good stuff. Important bringing it on the table!
> As with everything, the issue is twofold. In the case of Heroku and your user-base it is reasonable to expect your users are closer-to-wire than an iOS guy hacking in Cocoa Touch.
Certainly! Regarding the overall question of "SDK vs. custom HTTP wrapper" there isn't a right answer; different parties have different requirements and should use the right option for them. I'm not even saying that users of Heroku shouldn't have an SDK here -- they should absolutely have one if it will help them, and Heroku ships SDKs for just that reason. I was trying to make the point that me, as a maintainer of a Heroku backend service that talks to other services, would like the option of not having an SDK; like you said, I want to be as close to the wire as possible.
Thanks for the feedback and reading!
More layers of software equals increased chances for vulnerabilities and/or bugs.
On top of that, the SDK can handle some interesting dev tasks that everyone has to deal with regarding a REST API. For example, best practices with api keys / secrets, error handling, caching, etc...
SDKs like any piece of software needs to be evaluated before integration, if it is an SDK that doesn't meet your requirements for development, you build your own against the REST API.
I agree! And a well designed API can certainly help workaround many of these problems.
I would say that fully evaluating an SDK is a non-trivial undertaking though, especially when it comes to features like connection persistence. And then what if it turns out that the SDK doesn't support everything you want (which is often the case)? On the flip side - if a service has a good API, I can build my own wrapper which uses my own conventions very quickly. And that leads to what the main point of the article was meant to be: please design a good API so that I can opt out of your SDK if I want to.
BTW, I think you were looking for "moot".
I fail to see the point of this criterion. Even when you implement the client code yourself, you'd surely not copy&paste identical HTTP calls everywhere just so you can search for them. You'd use some form of encapsulation (modules, classes, subroutines). And those calls are just as hard (or just as easy) to grep for as calls to an external library.
I've found in the past, especially when looking being introduced to new codebases that I'll see URLs around in the logs but that the logging is weak enough that it's somewhat difficult to track down which module is initiating them. With thin HTTP wrappers a quick Grep will expose exactly where the offending code segment is, and I find that occasionally useful.
My experience though is that some don't, and figuring out whether they do support these more advanced features, and how to use them if they are included, often makes the discovery process longer than just implementing a simple HTTP wrapper. That of course assumes a reasonable well designed API with pretty good docs though.