NJL Heavy Industries

Services Implications

2014-11-06

There are many reasons why somebody might get enthused about building their application using services.

Conceptually, the real win with services is that they make APIs within your application much more real. Abstractions can still leak, but if you hide access to something behind a service, you have at least a fighting chance.

In the real world, your code is awful. My code is awful. All of our code? It's awful. I've never written a substantial amount of code without contemplating afterwards how much cleaner, smaller, and nicer I could make it, knowing what I know now. Frederick Brooks is alluding to this in "The Mythical Man Month", when he advises us to "plan to throw one away; you will, anyhow". I was once asked in a job interview about code I felt proud about. I might have been able to answer that question for the first few years out of college; I am far too aware of my limitations these days.

This is where the true beauty of services shine. Services are a single, isolated piece of gross.

There are the obvious things that you would say when talking about collections of APIs talking to one another. Services should be cohesive and loosely-coupled; you should be able to deploy and test them both in isolation and in concert. They should be documented. Services are a useful unit for monitoring, alerting, scaling, deployment, and backup. It frequently makes sense to map a few services to a development team.

There are some less obvious things, though.

Services should be designed to be replaced. If you're lucky enough to be successful, you're going to need to replace them sooner or later. Maybe you'll need to add features your design makes improbable, or you'll have an untenable scaling bottleneck.

I also strongly suspect that services should be scoped so that they could be rebuilt by a reasonable-sized team in a reasonable length of time. I'll leave the definition of reasonable up to the reader.

So how do you design your service to be replaced?

You should probably be talking to your service over a mechanism with support for lots of languages. HTTP is the obvious choice; as a bonus, you get lots of free tooling. Scaling an internal service with ELB or Varnish is a pretty nifty trick.

You should probably avoid language-specific serializations. JSON, MessagePack, ProtoBuf, and Swift all seem like good choices. You might not want to switch languages, but future you will thank you when it comes to debugging.

Finally, you should follow good interface design; document it, do your best to avoid undocumented behavior that clients always seem to come to rely on, and avoid leaking implementation details.

Some people think service-oriented architectures are about the plumbing; discovery protocols and whatnot. That's not the point. The point is keeping the scope of individual pieces small enough that you can understand, fix, and replace it in isolation.