Sensible Services, or Two Cheers for Microservices

I work in London, so sometimes I talk to cool startups where people occasionally produce micro-service architectures that require a factorial number of web calls to correlate two pieces of data. And sometimes I talk to people in the City who can’t change the length of a form field for fear of bringing their investment bank down. Obviously both of these are bad. I’d like to share with you some thoughts on avoiding either.

What is ideal?

An ideal business system would be purely functional, a single side-effect free function mapping from one state of an entire business to another. Beautiful nested monads cascading though time 🦄 If in doubt, go read What is Functional Programming? and come back.

And, if you have time to build everything in scratch using Cloud Haskell, OCAML or Erlang, it is possible to get close to this. It is very worthwhile to at least consider this, and companies that have gone down this path are very happy having done it.

What is modularisation?

On the Criteria To Be Used in Decomposing Systems into Modules is the most useful computer science paper I have read and it is a simple one. Parnas considered the purpose of modularisation to be information hiding. Hiding design choices, hiding domain knowledge, hiding things that were likely to change from the perspective of other components. (There’s a more practical explanation in Thinking Forth, (p23), and I strongly recommend reading the whole book.)

So the appropriate boundary for a software module is not a network stack, or a DLL, or a software library, or an API. Boundaries should be whatever best hides secrets. We’ll come back to this later…

Nothing new under the sun

“In pioneer days they used oxen for heavy pulling, and when one ox couldn’t budge a log they didn’t try to grow a larger ox. We shouldn’t be trying for bigger computers, but for more systems of computers.” — Grace Hopper

Man’s (originally woman's) desire to decompose business IT systems into multiple running services, is as old as computer networking.

In the early 00’s, we had Service Orientated Architecture, usually just SOA.

“Service-oriented architecture (SOA) helps organizations boost business performance, reduces costs and enhances the flexibility of business processes.” — Accenture
“It’s hard to recall the service management industry before the service catalog became common practice. Through our co-authorship of ITIL® Service Strategy, Accenture introduced the concepts of service portfolio and service catalog and the explicit definition of the term “service.” Our thought leadership helped define key concepts that have become the foundation of today’s tools and technologies.” — Accenture

SOA subdivided services into four classes [Functional, Enterprise, Application and Infrastructure], operating using SOAP/WSDL and linked across an Enterprise Service Bus.

More traditionally, my old university textbooks all had chapters for Distributed Systems.

Information Hiding Revisited

We have already decided that the purpose of modularisation is information hiding. What does carving a piece of our system out and putting a networking stack on top of it succeed in hiding? It isn’t for hiding logic. If it were, we would simply be sharing code, and we can use a library for this.

If we look at microservices or SOA or any distributed system design, these are all about hiding secrets like :-

  • Deployment Processes
  • OS Configuration
  • External Systems
  • Data Stores

Or in the words of Jeff Bezos…

1. All teams will henceforth expose their data and functionality through service interfaces.
2. Teams must communicate with each other through these interfaces.
3. There will be no other form of interprocess communication allowed: no direct linking, no direct reads of another team’s data store, no shared-memory model, no back-doors whatsoever. The only communication allowed is via service interface calls over the network.
4. It doesn’t matter what technology they use. HTTP, Corba, Pubsub, custom protocols — doesn’t matter. Bezos doesn’t care.
5. All service interfaces, without exception, must be designed from the ground up to be externalizable. That is to say, the team must plan and design to be able to expose the interface to developers in the outside world. No exceptions.
6. Anyone who doesn’t do this will be fired.
— Amazon memo as recalled by Steve Yegge

The information we are hiding is operational. We are sparing Team A, the need to know the deployment schedule of Team B. Or the programming language used, or letting Team C be someone in another company of whom we have no knowledge. In essence what we are hiding is any sign that the service originates within our own organisation.

Separated services are like sanitation, an enabler for a more complex society. They do not add value for single team performing a single task.

Why do we break up systems?

Because it is cheaper. Because we can economise on the time-cost of co-ordinating developers, or avoid hiring them all together by deploying ready-made solutions. We’ve already decided the elegant option was to re-write everything in Cloud Haskell 🙂

“Organizing into services taught teams not to trust each other in most of the same ways they’re not supposed to trust external developers.” — Amazon memo as recalled by Steve Yegge

Breaking systems out into separate services enables decoupling between teams and infrastructure. Both internally, to drive “business agility”. And externally, to drive commodification. In a well-governed IT environment, these the same goal.

This a Wardley Map for planning businesses strategy, which was drawn up by Britain’s HS2 rail project. In this context, we can see services as being a way to move functionality down and to the right. From something expensive, to something cheap.

For example, when I first started working, I was quoted for six servers to power a blog: two for redundancy multiplied by three separate environments for development, QA and production. (We used shared hosting instead for a ~70,000% cost saving.) Today most startups use webpage builders, like SquareSpace or Hugo, and most developers would consider maintaining a static content site to be a non-technology task that naturally belongs with marketing. At my latest client, I do not as a software engineer, have access to the company’s CMS. It simply never occurred to anyone this would be needed.

Separating services increases options to buy or outsource. It is a decision driven by economics, not elegance.

This is the same argument within, as between, organisations. In the UK and US, Amazon dominates online retail. To have a more efficient operation, Amazon’s shopping cart and retail payments are build as services and re-used across Amazon’s sites. In the Nordics, there is no incumbent, so Klarna provides an online shopping cart to a broad range of independent web shops. But the decision to use Klarna, or, of an Amazon unit to integrate their own payment processes, is economically and functionally equivalent.

Sensible Services

With this I would like to offer some suggestions for Sensible Services.

A sensible service encapsulates something that might be bought as hosted solution. That is not to say that your organisation would buy it, or that there is an actual market of such solutions. Simply that if it did exist, you would buy it.

That means that the service must be versioned, documented, supported, and have an expected quality-of-service. Not all products are services, something with no state or external interactions is best shared as a software library. But all sensible services are products.

So in a sensible services architecture, services will be :-

  1. Addressed by a common mechanism, e.g. DNS.
  2. Delivered independently as products.
    * Tolerates failures in other services.
    * Documentation
    * Service-Level Agreement
    * Independent release cycle
    * Client libraries (or compatibility with existing ones)
  3. Sharing one or more common media, ex. a messaging bus, gRPC calls, REST, SIP, etc.

We are rejecting hard-lines on medium choice. All choices are potentially valid, including replicated file systems or shared relational databases. (If they have been correctly configured for sharing, which all good relational databases can be. If it helps, try calling databases Shared Persistent Software Transactional Memory 🙂)

Implicit in this, is that most core services should be off-the-shelf products, like Active Directory for authentication, or even externally hosted, like GMail.

And with this, that there is a trade-off between reduce number of common media, and reducing the number of new services that need to be built. It is better to allow SYSLOG than to write yet another non-standard REST API for logging. Forcing all your services to be HTTP REST is counter-productive to the aims of a sensible services architecture. (As is forcing them all not to be.)

Service may themselves be many different running processes. Indeed to be able to have sufficient functionality to qualify as a sensible service, they would probably need to. Individual processes are not services if they are not supported as such.

Since services are supposed to be supported, this means they should come with client libraries. Without code sharing it is impossible to build internal services rationally. Code sharing, complete with a build systems and repositories, is a prerequisite for a sensible services architecture.

Kubernetes and Spheres and VMWare Oh My

Sensible services do not preclude (or discourage) the “traditional” micro-services approach of hosting a large number of containerised REST servers on a Kubernetes cluster.

Kubernetes allows us to run many heterogeneous programs over a homogeneous hardware cluster, reducing the number of moving parts in our hosting. It provides the benefits of Erlang, or EROS, within a familiar Linux environment. This is a-good-thing 👍

But there is no magic to Docker images. A container does not in itself constitute a product, or provide failure isolation. It is easy build a brittle system from multiple containers. Failures will cascade until they are caught and handled, regardless of the existence of an extra network stack. And no API is self-documenting.

The key to having reliable services is clean external interfaces and statelessness, e.g. the 12 Factors principles. Holding to the 12-factors is great for Kubernetes hosted services, but also gives an organisation the option of using PaaS hosting, like Heroku. Kubernetes is great, but why do work if you don’t have to? (Serverless solutions, like AWS Lambda are also very promising.)

What Docker images can do brilliantly is making “difficult” code conform to a standard deployment model! For example, Writing Docker Microservices in COBOL.

But any clean containerised/PaaS/serverless hosting solution is unlikely to cover all the needs of a functioning organisation. Working organisation have Excel users who must be considered. The advantage of a non-dogmatic services orientation is that we can consider both SharePoint and our newly built 12-factor apps together.

Two Examples and One Summary

Lending against Universal Credit

If you live in the UK, you are probably aware of the on-going Universal Credit roll-out. Although well intentioned, it is leading to significant interruptions to national insurace payments for households who are entitled to them. Which should be a very attractive lending opportunity. After all there’s little risk to the lender, the client will get the money to repay eventually. It is a genuine need, and it gives them a relationship to customer who might not normally borrow from them.

It would also be a time-limited product, eventually the roll-out will be complete, and one that would be best delivered in cooperation with local partners.

Without service architecture, a bank has two choices: extending an existing monolithic system, or issuing a line of credit to a partner who then deals with those loans. The first carries the risk of de-railing other IT projects. The second has monitoring costs, fraud risks, and is difficult to price.

With a services architecture, we can build this product by consuming the bank’s existing services for loan repayments, credit analysis, debt collection, etc. For example, a user might login to the loan product via OAuth2 through the sponsoring bank’s website, that would pass a token to the new loan service to look-up the user in the sponsoring bank’s credit service, the loan service would then do its own information gathering and pass back the proposed repayment schedule through a REST call, and so on. Our sponsoring bank might have delegated its own internal authentication to Google, so the loan product would allow logins to its back-office using SAML 2.0, etc.

In this case, it will not matter if the new loan product is operated by an external party, or by an internal business unit. That choice can be left with the sponsoring bank’s management, without artificial IT restrictions.

Back to School in Vietnam

There might be a fashion platform that has services covering user management, user billing, sourcing and shipping clothes, measurement, etc. And a small coffee-table fashion magazine covering the Vietnamese market, that has website. The fashion magazine might wish to run a Back to School promotion for August (start of the Vietnamese school term). They’ll name the platform and co-brand, but they don’t want to use another company’s user accounts. Also they want to appoint a technology partner to build and host the promotion website.

So in this arrangement, we might have a fashion platform hosted out of Shanghai, a magazine run on shared hosting from Singapore, and a web development agency in Hanoi. The agency can build their Back to School website using Django, while the platform is built on Java, and the fashion magazine stays with its heavily customised Wordpress site.

The Back to School site can allow the magazine’s readers to login with their normal user accounts via OAuth. It can then use widgets from the fashion platform to collect measurements, and access the associated services to work out which clothes to display to the buyer. With a purchase it might use its own Vietnamese orientated payment gateway, but then hand the order back to the platform’s own drop shipping service for fulfillment.

And none of this has to change if each one of these organisations was simply a unit of Amazon.

But in summary…

We could make similar examples for stock brokers, FX services, front and back-offices of investment banks, etc. But if you just sell custom socks on the internet, you might be happiest with a monolithic Django app on Heroku and a SquareSpace account 🙂

  1. Deployment options like containers or serverless are not suitable units of logic abstraction. They hide operations, not logic.
  2. The purpose of a services architecture is to increase commodification and reduce costs. Down and to the right of the Wardley map!

Further Reading (that supports my opinions 🙂)