A decade after working on my first Integration project, I’ve been reflecting on what changed and what hasn’t. My last post focussed on how things were then, and what technology and techniques have fallen away; but there is as much common as there is different. Much “best practice” in 2007 is still good now, and sadly some of the challenges I saw then are still to be truly addressed.
A big theme of the early 2000s was a push to use open standards - although older colleagues may point out that CORBA predated this. In my “what changed” blog I took a rather tongue in cheek attitude to the myriad of WS-* standards; but although this trend got a little carried away, it was essentially a good thing.
Back then committees decided standards, now a more Darwinian “survival of the fittest” Open Source approach has taken hold. Standards are still open but rather than a compromise several potentials emerge, then one - or perhaps two - win out. This was seen when Swagger, and to a lesser extent RAML emerged as the successors to WSDL. These can be more elegant (not having gone through the sausage machine), but there may also be less uniformity. We don’t all quite talk one language; but we are at heart still standards based.
Discoverability is still elusive
Last time, I discussed how HATEOAS displaced UDDI. In truth, there’s still work to do in discoverability. My personal experience shows HATEOAS links in REST APIs are sporadically implemented at best, and often ignored entirely.
At the extreme end of the scale, some REST evangelists suggest that apps using APIs discover available APIs at run-time (e.g. call api.host.com to get a completely dynamic list). This means apps don’t need to “know” about any “/resource” APIs; but I’ve never seen this (even as a POC).
Most organisations I’ve seen adopting a REST approach will easily reach Level 2 on the Richardson Maturity Model but many stop there and are happy to hard code their apps to use fixed APIs (host.com/something). Others go one step further, using HATEOAS when most useful to link directly related items between APIs (e.g. products in a shopping cart to a product details API) but don’t link everything.
The truth is that a fully dynamic approach isn’t too helpful unless all logic is server side. An app which doesn’t expect an endpoint won’t know what to do with it even if it suddenly comes available. Even without that extreme, writing lots of HATEOAS links into an API requires effort to both develop and maintain. Linking only what is useful, rather than absolutely everything (attempting to be “pure”) is probably a trend which will continue, and it’s probably sensible that it does. This means most APIs will never fully reach Richardson Maturity Level 3 – but it’s good to keep pushing for at least some HATEOAS. Perhaps trying to get everyone to Level 2.5?
Versioning is still hard, and still overlooked
The start of too many APIs go something like:
Step 1: “Create an API”
Step 2: Want to change the API
Step 3: “oops I didn't think about versioning”
Step 4: “Create Version 2 of an API”
Versioning is very often overlooked. At one level it’s simple: try to make changes backwards compatible so you don’t need to introduce a new version, up-version when you do, retire the old one once all users have upgraded.
Sadly, it’s tricky when there’s lots of reuse, and webs of interdependence between APIs (the cost of HATEOAS again). This is true in large Object Oriented projects; it was true in large SOA Web Services projects; and it’s true in RESTful APIs.
In OO, reuse is achieved by distributing copies – so if people run old versions it’s not your problem. If you’ve got webs of interdependence within your own monolithic application you can at least (fairly) easily trace and refactor where things are used.
Services on a network are more difficult. If the service is only used by a couple of internal applications you can force them to upgrade (perhaps even as a big bang). If APIs used by external consumers you don’t control or apps given to “Joe Public”, then this can be really tricky. As API owner, you’re responsible for keeping them running – or suffering the fallout if you turn them off too soon. Tooling such as API gateways with developer keys can help know who uses an API, as can sensible logging; but even knowing who is using an API isn’t always enough.
As an example, if a company has four versions of a mobile app running on: Android, iOS and Windows Phone (a few do I'm told); then they have 12 apps using their APIs. Hopefully they had foresight to include functionality to force upgrades, or a kill switch triggering a polite message saying, “I won’t work anymore”. They probably didn’t think of that (versioning is overlooked) so they need to maintain all the APIs supporting these apps – or disappoint their customers by their apps suddenly stopping with no warning.
In some ways, the tooling is better than it was, but apps mean it’s more important than ever to think about versioning early.
Middleware still exists
This is sometimes overlooked in the present buzz around Microservices.
- Old view: Systems provide service, an ESB connects them (with discovery, transformation, orchestration and routing) – but scales poorly and is ultimately inflexible.
- New view: Microservices encapsulate single functions; provide well defined interfaces; do transformation; orchestrate other Microservices. REST allows discovery. All scales well.
This sounds great but has two problems: Microservice Architectures aren’t going to become the norm overnight, and Microservice architectures aren’t the best answer for every problem – so may never become ubiquitous.
Microservices are sold on their ability to adapt quickly and scale. Scalability comes in the form of high throughput, but also scalable development. Not having a shared database helps solve the former, breaking down the problem into small “two pizza sized” teams solves the latter.
Whilst all this is great, a microservice architecture has drawbacks: distributed applications incur a cost (multiple databases, added latency of more network calls, an “eventually consistent” data architecture) and can be more complex. In cases where scale isn’t that important, and where time to market isn’t key, the cost might not be worth it.
Areas of business differentiation can really benefit from an innovative Microservices approach – but commodity IT (finance, HR, supply-chain management etc) will likely remain COTS packages – which may never be re-architected into Microservices COTS packages. Add to that the fact that most organisations have Legacy products that they don’t plan to retire or replace any time soon.
In most cases therefore there is a need to integration between COTS/Legacy products – or need to expose a COTS/Legacy product to custom builds. In these cases, having something flexible in the middle is still useful. My last post discussed “The fall of the ESB”, but whilst it’s lost its golden place as a “MUST USE”, middleware software is still a useful tool in the toolbox for solving some problems.
Are services an asset or just part of a user story?
A common ideal (then as now) is that services/APIs are a reusable asset. A common reality (then as now) is that they are just something needed to deliver a particular widget or journey. Registry & Repositories, and API Portals have both tried to solve the technical challenge of re-use (finding an existing service/API and understanding it). As I highlighted last time, the fact that APIs are inherently open to the internet makes them reusable outside the organisation. But the design challenge remains. If a service is designed well, as a generic component, then it will be more re-useable than if it’s designed narrowly, in a rush, for an immediate need.
There are best practices (then as now) – most recently approaches such as Mulesoft’s “API led architecture” for layering different granularities of APIs; but ultimately this takes discipline. This works best if someone with authority (if not the CEO, then at least an Architect or a Product Owner) sees APIs as an asset and not just a bit of pluming to connect an app to its data source.
Data is central
Fundamentally Integration involves only two things:
- Moving data around
- Non-functional requirements (see below)
The only truly tricky integration challenges are where data doesn’t “fit” somehow. When trying to connect two systems and: a required field is missing; there’s no shared key; or the formats don’t match. Ultimately most integration design/build is data mapping and data flow; the rest is solving silly problems, or triaging defects which had nothing to do with integration, but integration got the blame until someone proved otherwise (guilty until proven innocent).
Finally... Fundamentals, and non-functionals
Some things which are provable (like CAP theorem). Others are obvious and common sense, once they’ve been pointed out; but are easily done wrong until they’re pointed out. Lots has changed in the last decade but most fundamentals have remained (and because they’re fundamental they’re not unique to integration).
The unhappy path is always followed eventually
Data can be sent at-most-once (try and give up), at-least-once (try, and retry but it might arrive more than once), or exactly once (but only if you use a product with transaction commit guarantees such as JMS – and even then there are issues). This needs to be thought about, and tested. REST APIs help by making as much as possible idempotent – although the consumer still needs to know to retry. POST APIs need thought about who is responsible for not duplicating records in case timeouts (clients by doing a GET to find out if it's made it, or servers ignoring replays - if it can detect them).
Non-functionals are key
Non-functionals could be a blog post in its own right, and this is already far too long. Suffice it to say that security, scalability, reliability, operability (including monitoring and alerting), latency, and maintainability are all important, both for individual APIs and for the whole platform.
The biggest mistake I made when first being exposed to SOA and Web-services was assuming we were reaching an integration end of history, and that there was a “correct answer”. I came out of university having been taught a lot of good things, but forgetting that all science is evolution and there are a lot of missteps and re-appraisals along the way. XML was “better” than what came before, it was an open standard which everyone agreed on, thus it would be that way from now on. Right?
What I didn’t see was that it was bloated, that end-developers hated XML DOMs, and that namespaces were overkill for most situations.
XML, and SOAP fell faster than one might have expected, but the fact that it was replaced by something new should not have been a surprise. Now I’m a little older, and hopefully a little wiser, I’m trying not to make the same mistake with the tools and techniques of today. They too have advantages over what came before, but they too have problems. “old” ideas like RPC are being talked of again, this time with new fashionable implementations – so the days of REST might be numbered. What I do expect is that at least some of the things which have stayed constant for the last decade (especially around data, fundamentals and non-functionals) to be around for a while yet.
My postings reflect my own views and do not necessarily represent the views of my employer.