065 iPhreaks Show - Mobile Backend Services, API Design & Hypermedia with Stewart Gleadow
The panelists discuss mobile backend services, API design & hypermedia with Stewart Gleadow.
Show Notes
The panelists discuss mobile backend services, API design & hypermedia with Stewart Gleadow.
Transcript
JAIM:
How do you stop yourself from flushing the toilet and watching it all the time?
[This episode of iPhreaks is brought to you, in part, by Postcards. Postcards is the simplest way to allow you to feedback from right inside your application. With just a simple gesture, anyone testing your app can send you a Postcard containing a screenshot of the app and some notes. It’s a great way to handle bug reports and feature requests from your clients. It takes 5 minutes to set up, and the first five postcards each month are free. Get started today by visiting www.postcard.es]
[This episode is brought to you by Code School. Code School offers interactive online courses in Ruby, JavaScript, HTML, CSS and iOS. Their courses are fun and interesting and include exercises for the student. To level up your development skills, go to iphreaksshow.com/codeschool]
CHUCK:
Hey everybody and welcome to episode 65 of the iPhreaks Show. This week on our panel we have Pete Hodgson.
PETE:
Good morning, from Berkeley.
CHUCK:
Alondo Brewington.
ALONDO:
Hello, from North Carolina.
CHUCK:
Jaim Zuber.
JAIM:
If I’ve learned one thing in this life: never play cards with a man named Doc. [Chuckle]
CHUCK:
I was expecting never get involved in a land war in Asia, but that works. I’m Charles Max Wood from DevChat.tv, and this week we have a special guest and that’s Stewart Gleadow.
STEWART:
That’s right, yeah.
CHUCK:
Do you want to introduce yourself really quickly?
STEWART:
Sure. I’m Stu. I’m an iOS developer and an API developer. I’m currently calling in at 2AM from Melbourne, Australia.
JAIM:
That’s like the future, right?
CHUCK:
Yeah.
STEWART:
I think that it’s Wednesday here.
CHUCK:
He’s talking from tomorrow. We really appreciate you getting up early or staying up late, or whatever you did – not sleeping.
STEWART:
That’s fine. Usually when I’m up at this time, I’m up watching Australia beat England on cricket something like that.
CHUCK:
Oh, there you go.
STEWART:
Sorry, Pete. [Laughter]
PETE:
I hardly understand how it works anyway.
CHUCK:
Cricket? Isn’t that an insect?
[Chuckle]
STEWART:
Yeah, similar.
CHUCK:
It’s a phone company. Anyway, you came on today to talk about APIs and mobile backends. I think – was one of them Backend as a Service? I’m a little curious, as we get started, have you used several of the Backend as a Service stuff?
STEWART:
I haven’t done much with Backend as a Service. I dealt a lot with mobile backends; I like to think of myself as an iOS developer, but most of the apps that you work on consume a bunch of different backend services. So, I end up spending a lot of time helping design the APIs and the interactions between the systems that the apps talk to. I’m not opposed to the Backend as a Service, but most of the ones that I’ve worked on have ended up being custom-built APIs usually deployed on similar - on Heroku, AWS, and things like that, but not at the really high-level where you get the whole backend integrated for you.
CHUCK:
Very nice. We talked about HTTP APIs – I think it was like three months ago – and we kind of gave some general guidelines. I know you said you listened to the episode, so I’m curious – do you have a good jumping-off point for us from where we left off?
STEWART:
Yeah. I think just in case some people didn’t listen to that episode, we covered the basics of Rest and Rest APIs, and going a little bit into hypermedia. I think the interesting part of Rest API is the way you really get good decoupling between the mobile finance and the backend APIs is when you start using hypermedia and putting links into your JSON really heavily, and trying to force that logic into the backend systems. So, maybe it’s worth kind of extending on that previous episode. I talked about hypermedia and some of the ways that we’re actually approaching and implementing it into our systems.
CHUCK:
Yeah, that would be awesome.
PETE:
Stu, do you want to talk a little bit about what your day job is, just to give people some context to where you’re building these APIs or consuming these APIs?
STEWART:
Sure. That’s probably a good place to start. I work for a company called REA; that’s Real Estate Australia. They make a website called realestate.com.au, which from the outside doesn’t seem that big, but on the inside it’s quite a big organization. I think there’s over 200 people in the IT section, probably about 800 people in the company; they’re global now. The primary business is putting real estate listings for sale or for rent, and share houses on realestate.com.au. We have an iPhone and Android app in the websites, as well. We build original iOS app, probably back in 2010 now, and build a custom API for that and obviously over the last four years that’s kind of evolved. The APIs have evolved; the apps have evolved, as well. There's now apps for commercial properties and there are apps for the Italian side of our business and some other places in the world. As it evolved, we certainly made plenty of mistakes in designing the initial API and how we chose to consume that. So, part of its history now is the lessons that we found, and where we found that those coupling with it probably shouldn’t have been, and where we found it we baked a bunch of logic into the iOS apps which probably could’ve been driven from the API, and maybe the app’s a little bit simpler as well. That’s kind of the context to these discussions. Probably about a year ago, we started moving to a completely new API or a new set of APIs to drive the data for these real estate applications. At that time, it was a good time to reflect and look at how we could have done the kind of Rest hypermedia better and maybe absolutely simpler and easy to maintain over time.
PETE:
Are these APIs just consumed by an iOS app, or I’m guessing, as an Android app or something like that as well?
STEWART:
Yeah. There are a number of applications consuming them. Nearly all of the APIs now have multiple consumers, so the iOS app is probably the primary driver of the hypermedia in the linking side. Purely because once we release an iOS app, if it’s coupled to specific details and structures on the backend systems, it’s very hard to evolve over time because we can’t force iOS users to update, even with the automatic updates with iOS 7, that’s still a really long tail of people; it’ll take a long to update to the latest version. So, making mistakes in how you design your API takes to be a lot more costly in that environment. Another way we're able to take a few more shortcuts, because when we screw up, we can actually – if we need to, we can deploy the website and the API together and everyone gets the new version.
CHUCK:
So your website is also a consumer of the API?
STEWART:
Yeah, over time. The parts of the website are generated from a giant legacy Java application, which is not consuming an API; that’s kind of built over the database with most of the application logic and most of the business rules all baked into a bunch of GIS base. We’re slowly moving away from that. We just found that – and that’s an interesting kind of architectural point that plays in nicely with the APIs is as we move from this one giant Java system, it’s very hard to deploy. We’ve actually put a pretty huge amount of effort into it to get it down from cordially deploys to every month to now every week. But it’s still a giant system; it’s very hard to understand and very hard to work on and maintain. As we develop new functionality, we’re actually splitting out small microservices providing the APIs for those systems and just static web frontends, mostly started in S 3, the buckets they're pretty simple. Over time, the live systems is slowly getting chipped away and we’re getting all these new APIs springing up that are getting consumed by the websites and can also be used by iOS applications. [Crosstalk]
JAIM:
Can you talk more about microservices? What does that mean? You hear the word going around quite a bit lately, but what are we actually talking about?
STEWART:
Microservices is mostly a buzzwood, I think. I remember a few years ago hearing a lot of talk about it from a guy called Fred George; talking it in GOTO and QCon, conferences like that. And they we’re talking about hundreds of services that were tiny and now are short-lived and that come and go, and now just all buzzing around, and it sounded amazing. But I have no idea how that would actually work in practice. I can't get my head around how hundreds of services would kind of coexist. So when I talk about microservices, I’m really talking about small manageable services built around a specific sets of functionality. We’ve probably currently got about instead of having this one giant backend with maybe we’ve got half a dozen new services, at least in the part of the business that I work in. I can see that turn into a dozen services over the next year; I can’t see it turning into a hundred. So just for a couple of examples, we have – some of the small ones are probably about autocomplete services; when you start typing in an address, getting back bunch of suggestions from that for users; that’s definitely a very small one. We have ones that deal with user authentication, and bookmarks, and management things like that. And we have other ones that deal with actually serving up the real estate listing to rendering the application. I think over time, they’ll probably get a little bit smaller and a little bit more focused. But when we say micro, they’re not really micro; they’re anywhere from a few hundred lines to code to a few thousand lines of code, but they don’t end up being these giant monoliths anymore.
JAIM:
Yeah. That makes sense. So you’re splitting up – you said – your autocomplete from say, like billing. You can update your autocomplete without having to go through the whole regular mode of deploying the whole thing that your app does. That sounded all right?
STEWART:
Yeah. If you end up breaking up the systems in the right way, it becomes really easy to work on the systems and deploy them. Many of these systems are deployed in everyday, some of them are deployed every time as a commit – it just automatically goes to a pipeline of tests and gets deployed to productions straightaway, and they become much easy to work on. But you do force some of the complexity into that integration side of things, so while working on your services really easy, you now have to be aware of how your service integrates with other services and in production and things like that. It is a little bit more complex to think of the system as a whole as being this number of moving parts now instead of just one thing. But I think if you break up your services right, you’re mostly just working in one place at a time. It certainly makes life a lot easier.
ALONDO:
How do you handle versioning in a scenario so that if you do make a change and deploy one microservice and another service depending on – or an app depending on it, making sure that it doesn’t break something else?
STEWART:
We try to avoid formal versioning wherever possible. Ideally, we make changes backwards compatible, and if the consumer is there we try to make a forwards compatible changes first and we try to avoid a formal versioning in all times. We’re using consumer-driven contracts for that a little bit. That was mentioned briefly in the episode from HTTP APIs, I believe. But instead of just building APIs from scratch, we’re trying to only build features into API that are actually being used by our frontend applications. To do that, we use these contract tests from the app. If you’re working on the app, you can say ‘I’m using these API and this is what I expect from it. I expect to do, say, this request and get back these kinds of responses with style, structure, these links present, and things like that’, and use these contract tests to actually drive out the APIs that is if you do need breaking changes, you’d start on the frontend and make those changes, and that would introduce a new contract, and then slowly roll that out for the web. That’s not too bad because you can deploy the frontend and the backend together if needed for iOS, we generally just make things backwards compatible. There are APIs out there that are serving multiple data formats to satisfy all their new clients. So whether that’s through putting a version in the URL or having content negotiation – it does vary between different services. There are even some cases where we were just left with all the API running in production and just forked it and started deploying a new version of it that new applications talk to and just leaving the old one there with the old format, as well. So there’s a number of approaches going on to do that.
PETE:
That actually makes me think of an interesting wrinkle with –. Normally, in consumer-driven contract, you just say, ‘this is everything that’s about to be deployed to production tests so that it can talk to everything that's already in production’. But with a deployed – or not a deployed, I guess an installed base of mobile apps, it’s actually multiple different versions of the software that are in production. Do you do anything to keep track of the old contracts, like this is the app from two months ago – expect these APIs to work in this way, and the app from four months ago – expect the APIs to work in this way. Is there anything formal like that, or is a bit more ad hoc?
STEWART:
At the moment, it’s a little bit more ad hoc. We’re still betting down our approach to consumerdriven contracts. There’s a little open source library that we’ve written called Pact, which should be on the realestate.com.au Github account, which is an approach for doing consumer-driven contracts. That’s a little Ruby tool for doing it. There’s another one called Pact-jvm, which is actually on a separate Github account for a company called DiUS for doing similar pact testing with jvm languages. We haven’t written anything like that for iOS yet; we started, but not to a point where we’re using it as part of our usual flow. That would be one way of keeping track of which versions we're requiring which data formats. We haven’t done that yet, with our old API, we deprecated probably about ten months ago now, but it’s still running in production for old versions of the app. We need to have that kind of which versions of the app that was using content tops in the API to do the negotiation and versioning and that had a bunch of - I can't remember if [inaudible] or aspect test. But it was Ruby-based API, so it was just down on the server. It wasn’t actually consumer-driven, but they were tests, kind of end to end tests checking that the old versions are still supported by the new version of the API.
PETE:
So, switching tasks a little bit something you said earlier that was interesting to me – you mentioned the website mainly deployed static components on S3. Does that mean when you’re saying that the website is consuming these APIs, do you mean like a JavaScript kind of app running in the browsers consuming them, or is that something in server land that’s consuming the APIs and turning them into HTML?
STEWART:
It’s a little bit of both. But for the newer sites that we’re working on in mostly static JavaScript heavy frontends built over the APIs, there’s very little initial service ad rendering. Even for those sites we often have some service ad rendering capability, say, for when people add a link into a certain page of the application rather than going through a flow from coming from the homepage and doing a search, and things like that. If they just hit a link in an email or in tweet or something like that, but it does depend on the system. That’s certainly for the new ones. It’s a lot of client-side rendering or at least rendering – maybe rendering partials on the server and stitching them together on the frontend.
PETE:
Is this some kind of SEO implications, as well, to doing it on –. That’s always what people say; that’s the big amount of people bring up when they talk about Angular or Ember or those client-side frameworks is that all your SEO goes out the window, but I don’t know if that’s actually true.
CHUCK:
I think it’s getting better, from what I hear.
PETE:
If they can all figure out how to run JavaScript.
CHUCK:
Well, they kind of own slash sponsor slash run slash whatever-you-want-to-call-it AngularJS. So, I’m assuming that they’re adding JavaScript capabilities to their crawlers.
PETE:
Yeah. That’s a topic for a different show.
CHUCK:
Yeah.
STEWART:
Yeah. You have to kind of be a little bit ignorant on the SEO side of things for this side. I overheard many discussions about it and it's moved into to this JavaScript-heavy world – and I think it’s certainly a little bit more tricky, but it sounds like some of these problems are solvable these days.
CHUCK:
Yeah. One thing that I have wondered about a little bit – and I’m getting into this myself with DevChat.tv, which will probably be released before this show actually airs. But I’ve setup a different service-oriented architecture with different services that provide information about different aspects. I’ve got an API system that serves up shows episodes and related information, and then I’ve got another system that manages all the sponsorships. Once I get that finished – incidentally, I’m using AngularJS on the frontend of DevChat.tv – I want to do an iOS app, and I recognize that some of the endpoints that I’m creating are probably ideal for the web app, but not necessarily ideal for an iPhone app depending on how I design it and what information I’m showing. How do you get that right without duplicating too much stuff or cross your API?
STEWART:
Certainly, we have hit some of those problems, for sure. We’ve hit areas where we wanted the data to come in a certain format from the API for the web client because it made it really easy to bind into the stash templates. That was one of the reasons we try and build these really high-level APIs that’s really little logic on the client and we can bind things into views quite easily. But we had different requirements from the iOS applications. They wanted to make it really easy to render on the client, it actually had a different structure – a slightly different structure. There’s certainly cases where we have APIs that are actually just sitting on both formats in parallel and hoping to use it to take care of some of the pack of load for us. I think, going forward, I’ve been talking to a few people about building – there’s a guy called Phil Calcado at SoundCloud that I saw he gave some talks about – he calls them backends for frontends or BFFs [chuckles], and instead of having a single API that multiple frontends talk to, actually having a single high-level API for each frontend. You might have one for iOS, one for mobile web, maybe one for Android, depending on how much the experiences vary on the frontend and how much the requirements changed. You can get away with using one, that’s great. But I found a number of cases where they start to diverge. And in this microservices world, you end up with lots of small low-level services dealing with certain tops of data on the backend, and you don’t want to end up with the, say, the mobile apps being the aggregation point for all these services. We don’t want that kind of coupling, so you end up – it’s nice having this one high-level API that stitches all that together and provides a view that’s very specific to the frontend. I’ve heard them call experience-based APIs before, so that they’re not just serving the raw data; they’re serving up the data in a format that really is driven by the experience of the frontend applications. I think when you go down that route it ends up making sense to have separate APIs for different frontend applications, unless the experience is exactly the same, and I haven't seen many places where that’s the case. Usually, the apps and the web tend to evolve in parallel, but not always in the same way.
PETE:
I think there’s a lot of Conway’s Law that plays into this, right? It’s normally the people building, unless you’re really doing the vertical team's thing that I think I talked about in previous episodes of having teams oriented around product features than almost certainly the folks building the website aren’t the same people building the mobile app. So, the experience is going to diverge naturally in that way, right?
STEWART:
Yeah, that’s right. We’ve been experimenting with those organizational structures over the last year. I’ve certainly been a big proponent of trying to make these vertical slash teams, so we’ve tried to set up around – fits the functionality, maybe in the Spotify model, although I’ve never talked to the guys at Spotify to see if we’re doing it same way as them or not. We’re trying to have a little team where you might have – you still have some specialists; you might have a couple of iOS developers, some JavaScript guys, some people on the APIs, and maybe some database people; altogether being how to develop features end to end. In reality, we still end up specializing a little bit. So, I spend most of my time on iOS apps and a bit on the APIs and there’ll be other people that specialize in other areas. You certainly do get a bit of Conway’s Law, but at least all sitting together and talking to each other regularly. From an API perspective, we often draw up on the wall what the requests flows are between iOS and the JavaScript app, and comparing them and constantly talking about that. We had found consistency, but they do certainly evolved independently.
PETE:
I think I’d like – I guess I said Conway’s Law, but I don’t think it necessarily needs to be a negative thing, right? You can embrace the fact that’s going to happen and use it to your advantage, and just say there’s a team building the iOS app, there’s a team building the web app, for example. They both need some kind of control over the backends, so give each of them a BFF, I guess. I really like that term.
STEWART:
Yeah, I think that makes a big difference. Maybe in terms of having those poly-skilled teams, it’s unlikely that the person that’s really great at doing the latest iOS view animations is also going to be the person doing performance genie on your database or setting up automated build pipelines to AC2. Some of them are the same people – if you know any of those people, we’d love to talk to them; they’re hard to find. But at least having some overlaps, so the people working on whatever frontend it is have the ability to work on the immediate backend that they talk to. So these highlevel APis – you should be how to work on iOS app and work on the consumer-driven contracts and the API that immediately that that talks to. Once that fans out into, say, half a dozen low-level services, maybe that starts to be different teams. You get at least some overlap in that discussion, and hopefully getting a bit more consistency and a bit more – an API defeats the purpose and it’s easy to use as possible out of doing that.
PETE:
In this model, do you always – I mean, it sounds like your iOS app is never talking directly to these backend small services. It’s always going through nothing else – some kind of proxy, or does it sometimes just talk directly to the backend services?
STEWART:
Sometimes it talks directly. One of the things we’ve been trying to feeL that way through is – because our APIs just use links to expose its functionality and for the app to navigate, you don’t always know from the app perspective which service you’re actually talking to on the backend, so we have a single entry point. Ideally, all the links are all existing data, so maybe you do a search and you get back some listings and you can get the details of them and send an inquiry to a real estate agent and things like that. It all links off that data, but there are a handful of standalone entry points out to the system that I’ve linked off into the other data. We had the single end point that we hit as soon the application starts up, and that’s kind of the app just asking ‘hey, what’s possible? What can I do? What functionalities are available?’ It hits this endpoint and then maybe there’s something in there for doing searches and something in there for signing into a user account and a few other things. Then we just keep following links from there. And even in that initial endpoint, they’re not all on the same service, so some of the authentication and bookmarks services are on a different endpoint to where we get suburb suggestions that kind of address all of the complaint and things like that. We do end up fanning out into other services. If it turns out that those services aren’t fit for purpose then we’ll certainly put something between to make something that is in a format we want and high-level for what we need in the UI. But as a default, if there’s an existing service that provides us what we want, see when we can get a contract in place to verify that ‘hey, this version of the app is expecting this data to exist’. Because we have a lot of things working on this different systems and it’s hard to keep track of which system are consuming which APIs, so getting those contracts in place is really essential so that once the apps are in production, people don’t forget about them and change them over time and break the app from three months ago which someone is still using, I’m sure.
PETE:
That’s such a really good example of where hypermedia can help you, right? You can start off by just pointing directly to a service and then evolve that over time. Even in the future, you realize you actually want to – and then maybe there’s a bunch of extra information being sent back that we don’t actually care about, we want to go through some mobile-specific API first, you can just change the link and you don’t have to deploy any new code to the client. Someone using an old version of the app gets that improvement as a freebie, right?
STEWART:
Yeah. I’m trying to think of a good example of where we did this recently. We had an address autocomplete service that came back and gave us verified addresses with a Geolocation with the latitude and longitude for the property. And it turned out the data source that what we’re using – but the Geocode has been terrible. I looked up a bunch of the places that I’ve lived in my life and it pins on the map on the wrong place. So, we ended up integrating another marker service in there that would actually wrap those two and provide the same address data but would actually get the latitude and longitude from another data source and merge those together and provide it to the clients. That kind of all just works seamlessly, and the fact that we are now talking to a new geocoding service rather than the raw address services. This kind of secondary and are unknown to the client; they just end up following the link and the initial sets of data. That was a nice example of using that and not breaking clients in getting that functionality for free, and even talking to a different service than we were before.
JAIM:
You mentioned one thing – a little bit before about when you’re developing these hypermedia APIs, the idea is that you can just follow links. So first in, your app should be seamless; you can change the functionality. You mentioned one thing that I thought was kind of cool to point out. You have one service where you can phone home to your various endpoints – your starting points. Is that right?
STEWART:
Yeah, that’s right.
JAIM:
Ok.
STEWART:
That kind of just grew out of the necessity that we’d love to have just a single URL that’s baked into the app and that’s the only place that phones home to get – and then it follows links form there. That’s certainly one of the principles that Roy Fielding talks about with REST with saying that the only knowledge you should have about the API is – he calls it a bookmark for a single URL; that’s your entry point. And then from there, it’s all about content types and hypermedia and knowing, you know, being part of that content discussion with the API and following the links from there. In reality, we got two sets of these entry points. One of them which is you start the app, anytime it always finds home. It’s the same point which is basically a set of links that comes back in the HOWL JSON format. And then from there, it follows links that can be really heavily cached, I think; probably cache it up to a day on the client. So even though it’s finding home each time you start it up, most of the time it’s just grabbing it off the disc. There is also a set of links that comes back when you sign in – basically, the links that require you to be authenticated. The initial links you get when you phone home are just part of the public API. You don’t need to sign in to the application to use those links. Then when you sign in, you get another set of links which will be more about how to retrieve a user details, or sync your bookmarks, and view your profile, and things like that, which require authentication. We know for those links that we actually have to deal with, whether it’s tokens of cookies or something like that, to deal with authentication.
CHUCK:
Do you use something like [inaudible] to do your authentication?
STEWART:
We don’t. We have an authentication endpoint and depending on –. We have different security models for different types of data. So certainly some data with our users, or maybe where they live is much more sensitive than other activity on the sites. We have – we use tokens for some things and then we use expiring cookies and things like that, and we reissue the first cookie on a very regular basis for other endpoints. The security model is being partly driven by trying to make the web a bit more secure, so it kind of uses cookies very heavily even for the iOS perspective. It’s not something that I’ve done before working on this system, but it’s actually seems to be working fairly well. I haven’t used cookies as an authentication mechanism from iOS before, but it seems to magically work at this stage. I’m sure there’ll be a problem.
CHUCK:
Do other question I have is it sounds like you’ve broken up the backend into several different applications or services. Do you have them all under one api.realestate.com.au, or do you – is it like users.realestate.com and houses.realestate.com and streets.realestate.com, and do you split up the API space over the services, or do you unify it so it all looks like it’s the same service?
STEWART:
They’re mostly unified. In our test environments, they’re often namespaced, so we have address.realestate.com.au and whether its bookmarks or listings.realestate.com.au, in production it’s all fronted by Akamai, so from the very frontend perspective, it all looks like it’s on a unified domain. Because of the way we use cookies, we actually require it to be on the same domain. In certain cases, to make sure the cookies get past through in the right way, that’s all fronted by Akamai and Akamai does some of that split to the different services on the backend. We’d love to get it to the point where it’s actually the same in every environment and works in the same way. At the moment, it’s a little bit different in production. To some of the test environment, it’s just in the way that Akamai does the unification under a single domain for us and splits it up all the way through.
JAIM:
From the client standpoint, it all looks like it’s one endpoint, even though it might be split up to different services?
STEWART:
That’s right. One of the decisions we made is we started moving down this hypermedia road is that all links that are provided are absolute fully qualified links. Initially, we wrote some links where they were just kind of server relative. They just be /user/whatever. That gets confusing on the frontend when you’re in the microservices world, there’s lots of backends that you’re talking to, and you’re not actually sure which one is which anymore. If one service provides a link that maybe backed itself, but maybe onto another service, it just makes life a lot easier if they just have the convention that all links are absolute, so we don’t do any kind of appending and stitching together of the URLs on the frontend. That makes life a little bit easy. All we know about from the URL perspective is this initial entry point, and from there we actually don’t know if they're on the same domain or not anymore. In some cases, we’ve only found out that they’re on a different domain when we’ve – fortunately, when we made testing before it released to find out that if these things are not on the same domain, then maybe some of the security policies on the cookies don’t work properly, so we had to unify some of them.
JAIM:
That was one of the issues I ran into trying to develop a hypermedia system. We use relative links; we use dynamic links; and I think we – I asked around if we came up with doing absolute links because it’s really not that much extra text. One question I had is a lot of the iOS libraries that do networking if you're using - we use RestKit, but hey, if networking does something similar, they all kind of require a base URL. So I know if we did a request for RestKit and it didn’t have the same base URL, it’s going to ignore it, so it never came back; we had to figure out a different way to do that. Did you run into any issues like that?
STEWART:
Yeah. We certainly ran into that same issue. Say you use AFNetworking when you make the AF network client or – I don’t know actually what it’s called, but AFNetworking 2, you feed in a base URL and it uses that. It certainly seems to be built on the Rails model, I guess, where you have a single entry point and then you have lots of kind of restful routes on top of that based on conventions and parts. It’s not really built for a hypermedia model. We do feed in base URL, but because every link is absolute, it never gets used. So, if you look at the internals the way it stitches together the NSURL. I think when you create a NSURL relative to a base URL; if it’s absolute, it just ignores the base URL. So, we do feed in the base URL, but it’s actually meaningless and inferior; it never gets used until someone deploys one of these microservices with a relative path, and I’m sure something will break.
JAIM:
Ok, very cool.
PETE:
You mentioned caching briefly, except you said caching because you’re Australian. [Laughter]
JAIM:
We need a pronunciation guide for all the –.
PETE:
Caching is my favorite thing that Australians say.
CHUCK:
I don’t know. I met some Americans say caching, and now I know who they’re talking to.
PETE:
No way.
CHUCK:
Yeah. Now I know who they’re talking to; they’re talking to Australians.
PETE:
When it’s from a particular part of the US, that seems – that’s like the canonical Australian thing for me.
[Crosstalk]
CHUCK:
I have no idea.
STEWART:
I keep on catching myself whether I’m going to say “route” or “route”. I’m not actually sure what people say in other parts of the world, but route has different connotations in Australia, so we say “route” for that. [Chuckles]
PETE:
We say “route” in the US, too.
STEWART:
Ok. I’ll stick with that.
JAIM:
Debatable, I think.
ALONDO:
Yes, it is.
JAIM:
There’s a Route 66, and there’s a rural route. So, what do you do?
PETE:
Really? Wow. Maybe it’s just because I spend all my time in California that I think everyone in America is the same, but they’re so different. [Crosstalk]
CHUCK:
That’s ok. Everybody outside the US thinks that the rest of the US is really close to California.
PETE:
Yeah, may British friends think I live in LA for some reason. I don’t know why.
JAIM:
Probably less confusing to say route in the US, even though I said route for a long time.
PETE:
So, that’s settles that. [Chuckle]
JAIM:
I put my foot down. You have a problem with it, talk to me.
PETE:
We get a lot of angry and hate mails from the listeners now. So anyway, caching – is that something you’ve had to work on a lot, because one of the things that maybe people think about when they think about all these small services is super chatty APIs and all that kind of stuff. If you have to do a lot of work to get caching to work from a performance standpoint, talk about caching in general, actually, because I don’t think we talked about that much in the other episode.
STEWART:
Ok. We don’t do anything really out of the ordinary with caching – I’m going straight back to caching. We try to the client-side caching when we can, so if data we know doesn’t change very often, we’ll certainly set the headers on that to say don’t even bother going back to the server if it’s within 60 seconds some things, or maybe it’s a day for other things. If things don’t really change very often, then we’ll set really long cache expiry time for them and let them sit on the local cache on the iOS app. And then to try and relieve load from the backend servers we’ll do some kind of ETag caching and just send not modified responses if the data hasn’t changed just to relieve the load on the network, as well, and the amount of download that the iOS app has to do.
PETE:
When you say – sorry, I think this is one of the things that are obviously we’ve been doing it for a while, but maybe isn’t obvious for people I haven’t done it before. When you say we just set the headers or the side, they’re the caching policy, you mean with HTTP headers that are driven or defined by the server, right? You don’t have anything baked into the app that says ‘oh, for this URL, cache it this way; and for that URL, cache it another way.’
STEWART:
No. we just using stock standard HTTP headers for all that and relying on AFNetworking in the NSURL cache under the hood to do its job and it seems to do a pretty good job of that and following, detecting, if not modified, and fetching a notification and things like that. If your device is running out of disc space, it’ll clear those caches and load them away, but in general there’s nothing baked into the app to make that happen. In some cases, it’s configured in the actual microservices. Ideally, I think the person who’s producing the data has the best knowledge about how stale something can be and still be relevant. But in some cases, we’ve configured our caching through Akamai, and I’d like it all to be controlled by the individual services. I think that it’s easier to reason about. But in some cases I’ve gone through, I’ve just done a wide pass and set up a bunch of caching for certain resources in Akamai letting it do its job as well. The one thing we haven’t – I’d like to play around with on that side but haven’t at this stage is so we say cache things really heavily, and then we decide that the cache is too stale, we go back to the server and try and get the – hit the endpoint again and get the fresh data. If that request fails, I’d love to just, you know what, even though the thing that’s stored on the disc is a little bit stale, you can have that anyway. Serving up stale data is better than serving no data. I haven’t looked at that yet about making that work in a way which doesn’t require much overhead. I don’t know if there’s a little knob that you can tweak that just make that work, or whether it’s something you have to build into your code that’s actually consuming the URLs. But that’s something I’d really like to do, and just because our mobile network, certainly in Australia, and certainly, if you’re not near one of the three major cities that we have, the mobile networks do get pretty flaky; you do get network dropouts all the time. If you have cached data, [inaudible] error page saying ‘sorry, everything fell over.’ I haven’t played around with that in much detail yet.
PETE:
I wonder if you can even get down that low-level however that’s underneath NSURL connection somewhere that where you’re not allowed to go with, you have to replace a bunch of stuff that you get for free at the moment.
STEWART:
Yeah. Maybe you have to implement your entire approach to client’s location. It’s not something I’d like to do. It maybe that it’s just a few things that you can tweak to detect when you get a – just detect if a resource is in the cache even if you get an error, but I haven’t try to do it at this stage.
PETE:
I thought the same thing because I definitely always preach the gospel of just use HTTP, caching headers, and the frontend stack; the client-side stack will do all the caching for you and it will just magically work, and that’s totally true. But it does mean if you want to do something which isn’t totally legit from an HTTP point of view, but it is helpful for the users, then you might hit a point where you’re kind of in a glass ceiling of like ‘sorry, you’re going to have to rebuild it from scratch yourself’.
STEWART:
Yeah, that’s right. One thing on the caching point – when we talk about these backends for frontends – that certainly makes life a lot easier. One thing we’ve noticed is as we start to draw up on the wall would be all the network calls out, maybe for a certain piece of functionality, you suddenly find out that for a certain page that you go to that it’s doing five or six requests every time. And even if they’re cached, if you don’t get that quite right, on a mobile network if you fire five requests all the time just to view one page, one of them is going to fail and you have to deal with a graceful degradation, or things like that. These backends for frontends, we’re trying to make the service a little bit more chunky rather than chatty, so you actually just go and ideally just do one request and maybe that will fan out and do the five smaller requests and bring back a single payload and it becomes more – you either get all the data or you get none of it. Then those can be a little bit more heavily cached, as well, to make life a little bit easier on the frontend.
ALONDO:
I have a very limited understanding of use of hypermedia, but I was just curious – when we’re talking about this caching, are there scenarios where you persist any information to disc at all, or simply just rely on the cache in making API calls?
STEWART:
I go back in and forth on this. I love the idea of really simple frontend applications and having as little code in the actual native applications as possible, because they’re most expensive to maintain; and leaving them to do where they’re good at, which is rendering the user experience and doing their view animations and things like that. I’d love it if it was all just based on HTTP caching and you just did a request over time. In reality, it doesn’t make a big difference to some parts of you have a local cache. Our application at the moment uses Core Data, and for a local cache, if you’re just coming back from – if you’re coming back to a screen that you’ve already been at, it will render it straight out of Core Data. It may then trigger a refresh in the background; it will actually go and hit the network and refresh the data that way, as well. But in general, most of the data that you’re looking at on anyone’s screen has at some stage being retrieved from the network, but is now being served up out of Core Data, except that we don’t do any images for that; we just let the HTTP cache do its thing. The images – we only just store the actual URLs to images in Core Data, and the rest of them are refreshed remotely, and the images have huge cache expiry time. If an image changes, you just change the URL rather than expiring the cache or anything like that. We do use Core Data really heavily for that. And the reason I go back and forth on this is because it doesn’t make life a lot more complicated on the client when every time you upgrade, you’re suddenly dealing with migrations and what’s the existing data state and how do you convert one data format to another; how do you deal with multithreading in Core Data without crashing stuff all the time. I know it’s possible to do that, it’s just really slip up if you’re not careful and it does make life a lot more complicated than just doing raw HTTP requests and dealing with things in that way. So I go back and forth with the moment using Core Data pretty heavily on the client side for that.
JAIM:
What does the data look like that you’re actually saving to Core Data?
STEWART:
We don’t have a really in-depth data model. We don’t have a crazy Core Data model file with lots of entities and relationships and things like that. We have a handful, but we generally really have –. I generally have a much flatter hierarchy of object on the client side that I don’t on the server. So, often the data that the server’s giving back into JSON is somewhat tied to database tables as much as we’d like it not to be. That often ends up being the case. On the client, I might only care about there might be an entire structure for an address of the property. But all I care about in this particular place is the street, so maybe I’ll just have a string for the street other than actually having an address object and things like that. We have a couple of high-level entities and just a bunch of properties. Because most of our applications is about rendering and viewing real estate information, a lot of the data that comes back in the JSON is strings, even for things like a price for a property and things like that, they often come back pretty formatted strings that we just display on the screen because we don’t do a lot of client-side processing with the data, so we don’t need the raw information very much. We actually went down a path of some of that Core Data object have – I can't remember what the name for it is, but we basically, we have sub-objects which automatically archived into Core Data just using NSArchiver and NSCoding. We thought that would be an easy win. I think over time, that’s been a real pain. I think if you’re going to go down the Core Data approach, I’ve used the built-in types or actually create entities and relationships, so I think putting the automatic archiving of objects oF some sub-objects, say, an address would be archived inside the property. Now, that’s probably a mistake; that’s been a little bit of a pain to maintain over time. I think I’ll move away from that and make our client-side model a little bit richer in Core Data.
JAIM:
So you’re persisting more of the property that result to it, not necessarily what was received from the HTTP request? Does that sound right?
STEWART:
Yeah, that’s right. We’ll do a search, for example, and we’ll get back an array of results with pagination links to get the next page of results, or to a self link to refresh the results. We actually get that raw JSON and we convert it into local Core Data models. In some places, we’re just using fetch results controllers and things like that, to actually fetch it and display it to the UI. That’s something we’re doing at the moment with some upcoming features. We’re playing around with the way that that works. But certainly at the moment, we rendered into the local objects and we don’t keep the raw JSON at all depending on how the caching is set up. It’s likely to exist in the cached directory on the disc some way, but we don’t look at that directly at any stage. Certainly the way that that information is parsed is one of the things that influenced the decision to go for absolute URLs rather than just relative to the server, because the place where we’re actually parsing the JSON and using it and following the links is often not the same place that you’re actually just doing the network call. So, if you have a server relative url/user/something, you really just know what server that response came from to be on a stitched-together URL to know where it is. If you want to do an absolute request, that means that wherever you pass those JSON responses, whichever object does that, you have to make sure you always pass in the base URL that the response came from, so you don’t have to stitch together relative URLs. When we started doing that, it was one of the points where we just said ‘no, let’s just make everything absolute URLs; keep it simple on the client’. We just followed the links directly.
PETE:
With the migration issue that you mentioned, is there anything to stop users – all this stuff is transient data that you could be pulling down from an API, or is there anything to stop you just nuking the DB each time you need to migrate rather than migrating just like clear out and just pull out all down next time you hit the API?
STEWART:
That’s certainly a valid approach. We’ve only been using Core Data in the applications since we moved into this new API in the last year. So far, we haven’t had any really hairy migrations where we thought we need to throw away all of the data. In the previous evolution of the app, it was just local objects that were serialized in a different format. And in that case, often when we detect this migrating from an old version, we just pull away the data and refresh the search and do that again. We haven’t had to do that recently, but it’s certainly a valid approach. There question marks about if you throw away the data in trying to refresh it, when someone’s upgrading, if that request fails, what do you tell the user? Or do you just show them app with no data and tell them to start again. It’s not a great user experience, but if it makes the developer’s life easy enough, in some cases maybe that’s the correct decision, if it’s not something you do regularly.
JAIM:
I’ve got the next trend in client-side development: Micro DB architectures. [Chuckle]. Level way, we don’t need them anymore. [Crosstalk]
PETE:
I’ve got one last question that’s been kind of rolling around in my mind while we’ve been talking about all these stuff with microservices is you said near the beginning of the episode that the tradeoff with small services versus monolith is – one of the trade-offs is with small services, there’s a lot of infrastructure and there’s a lot of moving parts, because obviously you’ve got more than one piece. Has that been painful for a mobile developer, he doesn’t really care about all of these moving parts; they just want an API to hit. Does that cause pain, and is there tricks that you’ve discovered from making the life of a mobile dev easier when they’re in this world of lots of moving parts?
STEWART:
There certainly is some pain to it. And something that we’ve been playing around with is we like the idea of these services is we had some technical freedom within each one. Some of the older ones are in old-school Java; some of them are – a lot of the newer ones are in Scala; a lot of Ruby on the backend, as well. Even yesterday, I was asked trying to make some changes to make a couple of APIs consistent with each other to make life easier. For the client, I ended up with, I think, Java applications, like a JavaScipt web app and two Scala applications all running locally on my machine, and then writing the Objective C to consume it at the same time. That’s a big load on your brain, and they’re all using different build tools, different languages, different IDs; that kind of gets pretty tricky and not everyone likes to do that, and likes to work with that many languages all the time. That’s something we need to be conscious of. One thing we’re playing around with in that is using virtualizations to make that easier. So whether you’re using Vagrant or Docker or something like that to make spinning up local dev environments really easy, because installing some old version of Nokogiri on a map when none of you compilers match can be a real pain.
Having all that stuff automated for you certainly makes life a lot easier. But that is a trade-off at the moment, and we’re trying to get to the point, at least from the outside, APIs have certain conventions and consistency in the data formats and the way they deploy and monitored, but on the inside, have it written in what language. There’s still complete freedom in that. The teams can choose whether they want to use Scala or Ruby, or they want to do all the new stuff in closure or something like that to let them have that freedom. It doesn’t mean that you start – you need to have a team of developers that are fairly comfortable at picking up new languages or at least interested in working with other languages when they get the chance.
PETE:
But I guess part of the reason I asked was because we’re going through a fairly similar thing at my current client of dealing with the complexity of all the moving parts in different languages and trying to get something in Scala to compile with a version of SPT and blah blah blah. I think we – it sounds like we’re going down a similar path. We’ve been looking at it while we started using Docker to just containerize each service and each service and have a little tool that will stand up; we have ASI pipeline build containers for each different service quite early on the pipeline, and then if you’re a dev working on service x that depends on some other services, then you just pull down containers that contain all the other services and then fire them out with this little tool and then you don’t really have to care whether they’re written in Scala or Ruby or whether they’re using Scala 2.10 or Scala 2.9 or whatever.
STEWART:
Yeah, we’re doing a similar thing. There’s a load to that, but I think hopefully that’s part of that trade-off with them being able to write these services in a language that’s fit for that purpose and that the team that’s primarily working on that service really likes to work in. Whether Scala is the right technical choice, or Ruby is the right technical choice or any others for an API. It’s one consideration from a technical point of view, but also what the team – more and more, I feel like teams are productive working on things that they enjoy working on regardless of the technical considerations. Maybe if your team really wants to use Scala all the time at work, maybe they’ll be more productive writing one of these services in Scala, not because it’s a better language or better technology, but just because they enjoy it. I think that’s one of the nice things that microservices is you can start to accommodate different people’s technical interest at the same time as delivering business outcomes.
PETE:
I wonder if anyone started writing a Swift-based web framework.
[Laughter]
STEWART:
I bet they have. I’m a little bit behind in Swift at the moment. There’s a talk tomorrow night at a local YOW! Conference, but it will already have happened by the time this episode comes out, but it’s going to be my first look at Swift in probably three weeks. So I’m a little bit behind on that.
PETE:
It’s kind of hilarious not having looked at it for three weeks is behind the times.
STEWART:
It’s changing really fast. Every beta that comes out, they make breaking changes, which is really great. I’m glad they’re willing to evolve and make decisions and not try and make everything backwards compatible at this stage. I’m glad that they're being aggressive about it.
PETE:
I agree. They’re evolving swiftly.
[Chuckle]
STEWART:
That can be your new blog, or your new Twitter account.
PETE:
I really want to make a tool for – a little DSL for building UI table views that’s going to be called Table Swift. And that’s going to be spelled T-A-Y-B-L-E and it’s going to be awesome. I’m not sure if that’s an American culture reference, but –.
JAIM:
I have no idea what you’re talking about. [Laughter] Not that I don’t have any culture, but Taylor Swift – you guys?
ALL:
Oh!
PETE:
Ok, maybe not. [Laughter]
JAIM:
Here we go.
PETE:
Maybe when it’s written down, everyone will find it hilarious.
JAIM:
I think I’m old enough yet to explain pop culture references.
PETE:
I have no idea who Taylor Swift is.
ALONDO:
I think we all aged out of that demographic.
[Chuckle]
CHUCK:
No longer cool. Alright, should we get to some picks?
JAIM:
Let’s pick.
CHUCK:
Alright. Jaim, do you want to start us off with the picks?
JAIM:
I will start with the picks. I started listening to this album a couple of weeks ago by a band called Trampled by Turtles; they’ve been around for quite a while; they’re a local Duluth band. They’ve been doing fairly well, but they were on Letterman a couple of years ago and kind of blew up; they’ve been making records for about ten years. The new album is really fantastic. It’s more of a melancholy take on what they’ve been doing. They always try to catch the live high-energy thing on disc with mixed results, but this is more laidback. It’s produced by Alan Sparhawk from Low also of Duluth; but it’s a great album, I think. Dave Simonett’s really a strong songwriter and that music is – it’s one of the few bands that sounds familiar and fresh at the same time the first time you hear it, so check out Trampled by Turtles and their new album Wild Animals. That’s my pick.
CHUCK:
Cool. Alondo, what are your picks?
ALONDO:
Ok. My first pick is a book and it’s actually influenced by last week’s episode. I picked up the Clean Code book from Robert Martin: A Handbook for Agile Software Craftsmanship. I was so taken by the subject matter and wanted to delve in a bit deeper. My second pick was actually influenced by – I started watching the Matrix movies again and I’m trying to see if I’m going to make it through the third one, because the first time I saw it, I almost walked out of the theater. But there was a little site that I found hilarious called Hacker Typer – sort of like what you see in movies; everyone’s typing and they’re typing furiously using all these information show up on the screen. It’s a fun little diversion. Those are my picks.
CHUCK:
Cool. Pete, what are your picks?
PETE:
I like Hacker Typer. I think that’s the one I’ve used in the past; we had a lot of fun with that other pairings station once [Crosstalk]
PETE:
I think it has like a bunch of drop downs. You can choose what stuff it produces, or you can even cut and paste random texts in there and then play along the keyboard and stuff comes out the other end. I think that’s the same thing. Anyway, I have four picks. I’m going to pick HAL, which – I don’t know if we actually talked about it specifically, but I know that Stu is using it at his day job; I’ve been using it at my day job. It’s a nice lightweight way to specify a hypermedia API using JSON and it’s got some quirks like all of these things do but it’s worked out pretty well for us. A couple of things that Stu touched on that I recommend for folks who maybe not super into the backend world, Vagrant is a really good tool for making standing up a server in an automated way so that you don’t have to tell everyone the five steps involved in getting the new service started, or the 20 steps, or the two-page wiki document, or whatever. You just define how to stand up a virtual machine in code and then just tell someone ‘check out this code’, and run Vagrant and it’ll stand up at the end. Docker is a more sophisticated version of that, kind of-ish or at least you can use it that way as a way to package up, not just your application, but your application plus all these dependencies in a container that you can then run in a host environment. Those two are really cool tools – Vagrant and Docker. My last pick is actually a JavaScript Jabber episode that I listened to recently. I actually don’t know how recently it was recorded, but it’s the one with Steve Klabnik talking about hypermedia and stuff like that. So, if you’re interested in all of this discussion of hypermedia, he’s a
super smart guy and it was just a really interesting conversation. Obviously it’s a bunch of JavaScript developers rather than a bunch of iOS developers, but it’s all the same. So yeah – JavaScript Jabber episode something, I’ll figure it out and I’ll put it in the show notes.
CHUCK:
It’s episode 104; it was a couple of months ago.
PETE:
Thanks, sir.
CHUCK:
Alright. I’ve got a couple of picks. One of my picks is also Steve Klabnik’s special. It’s designinghypermediaapis.com and it’s actually an ebook – if I can spell it right when I type it in; but it’s pretty good and explains in depth what hypermedia APIs are and how you want to structure them and things like that. They’ve also been working on a JSON API format, and I’m trying to remember – yeah, here it is.
PETE:
Kind of like an alternative to HOWL or [inaudible] or one of those kind of things. .
CHUCK:
Yeah. And he talks about that in that episode that Pete mentioned, and it’s pretty interesting, as well. Then finally, I’ve lately moved my development situation over to my laptop. It’s just nice because then I can just pick up my laptop and go somewhere. It seems like once or thrice a week, I’m at a Starbucks or another restaurant that has WIFI just to get out of the house and change my situation a little bit. The other thing is I’ve got this standing desk that I’ve put up, and I have one of those standing desks where you move your laptop over to the standing desk to stand up – that’s how it adjusts. Anyway, I wanted something where I can just plug stuff in and go, so I’ve tried out two different docking stations for my MacBook Pro. I’m going to put links to both on the show notes. One of them is this little silver thing – it says it has two Thunderbolt ports on it; what they don’t tell you is you need one of those Thunderbolt ports in order to hook it to your laptop. The other one looks like it had a Thunderbolt port on the front and then two on the back, and it turned out that the one on the front was actually a USB 3 plug, but it has an HDMI plug on it – connector, whatever you want to call it – HDMI output. There we go. Now I sound smart. I’m liking that one a little bit better because I only have to plug one thing into my laptop to get everything to come up right. I just set the arrangement on my laptop and it seems to remember it when I plug one in or the other; that’s pretty nice. I’ll put links to it in the show notes, as well. They weren’t really cheap, but it’s more convenient and I feel like I can work standing up for part of the day and not have to change gears too much. I just unplug two plugs from sitting down and plug one in standing up. So those are my picks. Stu, what are your picks?
STEWART:
I actually mentioned the pact library for consumer-driven contracts. I think we got links to it from earlier in the show. It’s not the only way to do consumer-driven contracts, but it’s a way to do contract testing for APIs, which I quite like. I like to mention the YOW! Connected Conference, which is a mobile conference happening in Melbourne in September. If there are any listeners in this side of the world, September 8-9 – I think Ben Sherman has been on the show here I’m pretty sure he’s coming over and speaking at that conference. And it’s iOS, Android, and into things and fun little devices and things like that; that should be a great local conference. And I thought the tips were just meant to be about beer based on previous episodes [laughter], so I’ve got an alcoholrelated pick. Having grown up in Australia, I thought the only red wine was Shiraz, and the last year, I discovered Cabernet in the Margaret River region over near Perth in western Australia makes amazing Cabernet. It’s not cheap, but it’s not super expensive. In 30 or 50 dollars, you’ll get some fantastic wine. So I just want to put in a plug for Margaret River Cabernet and I don’t believe that all the Australian wine that we send to other parts of the world is all that’s available. We only send the bad stuff overseas. [Laughter]
CHUCK:
Very nice.
PETE:
That’s our first wine pick. So high class!
CHUCK:
One other thing –.
JAIM:
I just love the yellow tail; keep setting that stuff over.
STEWART:
You can have it.
CHUCK:
One of the things I forgot to mention is if you are in Utah and you want to learn Swift, I started a study group a few months ago, and we’ve been picking books that we read and work through. The next book that we picked is the Apple Swift book. We’re going to work through it one chapter or section at a time. It’s probably going to take us several months to get through it, but effectively it’s kind of a self-study learn iOS/Swift. If you’re interested in that and you can make it to Lehi, Utah, then check it out. And if you’re interested in having it done as kind of a Google Hangout or something like that, I’m not sure how well it will work sitting in a Starbucks, but I’m willing to give it a try. So if you want that done, send me an email at chuck@devchat.tv.
[Hosting and bandwidth provided by the Blue Box Group. Check them out at bluebox.net.]
[Bandwidth for this segment is provided by CacheFly, the world’s fastest CDN. Deliver your content fast with CacheFly. Visit cachefly.com to learn more]
[Would you like to join the conversation with the iPhreaks and their guests? Want to support the show? We have a forum that allows you to join the conversation and support the show at the same time. You can sign up at iphreaksshow.com/forum]
065 iPhreaks Show - Mobile Backend Services, API Design & Hypermedia with Stewart Gleadow
0:00
Playback Speed: