Hacker News new | comments | show | ask | jobs | submit login
Why I avoid SDKs in production (brandur.org)
73 points by brandur on Feb 4, 2014 | hide | past | web | favorite | 43 comments

That's cool. You're not the guy we built it for.

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.

Or, in a rather less condescending tone, you might have built the SDK for Busy Bob, who is using your API, which is is important but peripheral, to the problem he is writing software to solve.

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.

I agree. I would just add that there are lots of Busy Bobs, all of whom just want to consume your service, and don't want to have to bother with creating their own JSON/XML/EDN/etc marshaling/unmarshaling code, translating HTTP error codes to whatever idiomatic error-handling construct is used for the language at hand, etc.

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, ...).

Wow, what a pain.

However, creating your own SDK is pretty much what the OP suggests--it gives him the level of control that he needs.

That's a great point, but I'll take it further. Even if you know how to build all that stuff, you still have to build it, there are a lot of decisions to make up front. Depending on the company (Stripe comes to mind), I might choose to trust the SDK's decisions by default and only move away from it when I have specific pain points. A lot can be solved simply by isolating the SDK's usage inside a wrapper that defines our application's usage of the SDK.

I concede this point. The article was written from the perspective of a user who's running a high volume app and wants maximum control over how they communicate with foreign services (i.e. my perspective).

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!

In reading the title of the post in question, as well as this comment, I'm going on a bit of a brief tangent:

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!

Fair point!

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.

> "... 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."

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'll assume you missed the "server-side" in the above, or that I wasn't clear in what that meant.

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 wrote a thing to start & stop OpenStack servers via JavaScript. HTTPS is a thing.

I have trouble thinking of any API that shouldn't be callable from a client.

Any API that uses some form of authentication token that is not obtained via some user-based interaction like OAuth may need to be at least proxied via a server side piece to prevent exposing credentials/keys.

Err.. why?

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 think he's saying that their API is callable from jQuery, they just don't want any more questions emailed to them about it.

I think it more was the junior devs who tried calling it with jQuery.ajax, but did stupid stuff like send the wrong content type for their data and then "Why isn't it working?! Can you send me an example?!"

excellent point...more developers really need to learn to take the business/owner perspective on the use of technology. The person whose money is on the line wants a working solution, not technical purity.

Even with the gratuitous dick-swinging aside, I don't think I could disagree more with this reply. SDKs provide a much needed layer between provider and consumer to prevent unnecessary coupling.

SDKs built by the provider have the benefit of getting a developer up and running quickly. The downside is that you really don't know the quality of the underlying code without some deep auditing and, as the author wrote, the underlying implementation is outside of your instrumentation, logging (etc.) framework. It's a black box you import into your app. This has nothing to do with abstracting the API . If you wanted to work with the HTTP API directly it you'd just abstract it yourself, because that's just good practice.

Even if I don't use the SDK, I greatly appreciate when the same company that writes the API also builds an SDK.

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 agree! We've been building a CLI that uses an SDK [1] against our new API and so far it's been tremendously helpful in identifying bugs and other problems -- if we hadn't gone through that exercise then we would have inadvertently shipped some of those issues out to our users.

I think SDKs are great. The intent of the article was to document why I will sometimes opt out of using them.

[1] https://github.com/heroku/hk

It's also good to have a test client application that the service provider supplied to be able to demonstrate that problems are with the service rather than with the client code.


The best of the best: build your API/service, build an SDK for it, and then build tools to manage the service using the SDK and service themselves. Not always possible, but very desirable.

Here's a suggestion: design the SDK such that calling a method returns you everything you need to construct the REST call yourself: the verb, the url, the query params, the form params, and perhaps some headers.

    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.

Great idea!

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.

Barf. Forcing boilerplate into the code of EVERY caller of your library "just in case" somebody might want to do something slightly different.

You know what I don't want in production?

Dual-column layouts that still require me to scroll to the bottom of the page, and then back up, and then back down.

Hah! Call it a design experiment, and either way it's good to gather a data point that someone hates it.

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.

Interesting. Before reverting, maybe you could try detecting page height and then flow from left column to right column with one page height then draw a horizontal rule under than and repeat left to right again with the next page-height's worth of text. I.e., instead of

  1 | 4
  2 | 5
  3 | 6

  1 | 2
  3 | 4
  5 | 6
...kinda like reading both sides of a book spine.

Nice idea!

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.

Oh, I think it's a cool idea--the issue is that for whatever reason I still end up having to scroll. As mentioned elsewhere, if you can detect the right number of columns/text, that'd be ideal.

I liked it.

Brandur, this is indeed very actual topic (and will be even more with the rise of APIs).

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!

I agree with the article. The last thing I want to do is add another layer of complexity to an already complex application.

More layers of software equals increased chances for vulnerabilities and/or bugs.

SDKs do have their value. I honestly think if the SDK is designed correctly and is open source, a lot of these points are mute.

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.

> SDKs do have their value.

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".

Including SDKs in production, as the author alluded to, is completely situational. It comes down to answering the question: can you write a significantly higher quality implementation to make it be worth your while? When it comes to starting out with a web service, a ruby gem or maven library, is invaluable. I love being able to work with an API within 5 minutes of setting up my dev environment. As other's also mentioned, it does cut down on support calls for the provider.

Another thing that client libraries often forget: configurable timeouts. Without them the default is around 60-120 seconds depending on the system.

> The Grep Test

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.

Yeah, I included this last as I think it's by far the weakest argument.

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.

We built a clients SDK on top of a regular HTTP library (as any other do). However if you want, you can get access to the parts of the library that exposes headers, http methods and other thing that effectively allows you to do work with the rest API directly if you want to.

Right. It's certainly possible, and it's awesome that many services like your own provide good clients that empower developers this way.

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.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact