Services
We are in the holiday season. You walk into any Starbucks and see the Christmas decorations. Lights and Christmas trees are everywhere, and the advent of code is still rolling. Whether Xmas is summer or winter, You can get some specials and flavors only on Xmas. Now, you must wonder if this is a technical blog post; yes, 100% it is. I promise there will be a point at the end. Let's imagine a very different coffee shop experience; in reality, it's not how I will describe it, but hopefully, it will help you to see my point. I'm pretty sure you've gone to a coffee shop before; how it works is simple: you look many for what coffee + snacks you want, you get in line, pay at the cashier, and then the barista will brew you coffee, warm your food, and you will be good to go. If it is a good place, they will call you by name. Now, let's imagine a very different experience. Consider this the upside-down Xmas experience. However, if you want to name it, I will give you my name after I explain it.
A different xmas Coffee shop experience
You cannot open the door. Yes, the door can only open if you show your ID on the glass. There is a camera that will scan your ID before opening the door. Next, to get in line, you need to know the ID of the coffee you will order. The coffee menu does not have names, just a bunch of IDs followed by prices like this:
10156 - 7.99
27#54 - 5.99
32X41 - 4.59
614f2 - 1.99
Now you think to yourself, I like the cool high-tech door; it took me some time to figure out how to open it, but now how am I gonna know what these numbers mean? The price is on the right, but what is the product? After spending 20 minutes staring at the menu, a Gentleman tells you that the first item is a cappuccino, mocha is the second, the third is americano, and the last is water. You said ok, now I will remember `10156` next time, is what I need.
When you are at the cashier, they ask for your ID again; after ten seconds, you calm yourself; just show your ID at the door, and you need to re-check security, says the cashier. Ok, you show your ID (again), and then the cashier asks for your bank code. You are perplexed and say I have HSBC; is that enough? Here is my credit card. No, we need your bank code. You google it and discover the code is `004`. You need to remember that they will ask you for the same code next time you pay.
Finally, you pay, 5 minutes have passed, and you (30 minutes already inside the coffee shop) get your order; the barista calls you the product ID `10156`, and you get your coffee. But when you do the first sip, it tastes very bitter. You realize there is no water, just coffee, and then you ask the barista, hey, there is just coffee here; where is the water? The barista says, first of all, show me your ID again. You show the ID for the 3rd time, and then the barista says you forgot to order water. You perplexed one more time and say, do I need to tell the obvious? The barista nods it; okay, you show the ID again to the cashier, and you say 10156 + 614f2, please. My code is `004` done. After paying, 5 min later, you get your coffee and leave the coffee shop.
Is this a good experience? It is secure. If you are an engineer, this coffee shop looks like an advance of code or coding dojo problem. You may like it, but day by day, this is an awful experience. Do we have the proper responsibilities and principles here? No.
Why it's so bad?
It's terrible because this is how our services look in reality in the industry. Remeber I would tell you I would call the coffee shop? Well, it is just a reality of distributed systems. SOA Services or Microservices, several times, the design is awful, the contract design does not exist, and very often leaks abstractions. I need to say something obvious that, still, we don't get it right most of the time:
A Service MUST abstract things and provide a better experience.
We have awful services in our poor experience (Menu, Cashier, and Barista). They require that much of the complexity is leaked to the consumer(in this case, YOU). I get it if you are building SDKs, Cloud Consoles, and Engineering level public APIs. You need to expose complexity to the client so they can have flexibility. It has complete flexibility, but several times, that's not what we are doing; depending on your business, most, if not all, of our services are not customer-facing. They are internal. Yet they can deliver awful experiences.
It's also bad because we throw all the complexity to the customer. We leaked lots of implementation details that should not be revealed. The minute I say a service needs to provide abstraction, there will be people rolling their eyes because people do not know much about bad abstractions, and abstraction is now a bad word. Microservices are not a bad thing, but people do so wrong that it usually seems like a bad thing. If done right, microservices are not bad, but I can tell you 100% that services are excellent, and we need services; we need abstractions, but good ones.
A service must provide abstractions; otherwise, what's the point of the Service? To remotely expose a table, a service must provide information hiding, encapsulation, abstraction, and benefits to the caller. Today everybody hates classes and service, no wonder why contract design is so so bad. Part of the problem was that when OOP SOA and even REST were at their peak, people blamed abstractions for the poor job people did. However, the problem is the lack of design skills, not the need for abstraction.
Yes, making proper design decisions is problematic if the product keeps changing its mind. Change is not bad. But has a price, We can't refactor as fast as we change(that's why we always need to do more refactoring). Avoiding abstractions is a good solution, but not because bad decisions create huge tech debt and might be as bad as poor abstraction.
Someone will say, eh, but here is the problem:
1. The business gives us no time to think; we just need to code as fast as possible.
2. There are a bunch of legacy, distributed monoliths or proprietary close source systems that we cannot change; this is how they work.
I get it, but we can do better. For instance, we could always add a service wrapper to a lousy service so that the service can abstract a bad contract and give us an interface to refactor the service under the hood (this is often called Facade on OOP or Strangler in the microservices world).
How can we make it better?
It's hard to hardcode rules (I will give more guidance in the next blog post with more concrete examples). But we can notice a couple of things in our coffee shop experience, which are:
- There is too much responsibility for the client: (The software design philosophy tells us the opposite - complexity needs to live somewhere, and it should be pulled down, not up - meaning the service should abstract things and byte the bullet of complexity).
- Crossing wires, cross-cutting concerns, and business: We require the ID three times, which is excellent for security but cannot be mixed with business functionality. In software, we can use HTTP HEADERS or even the SideCar Pattern to make this more transparent.
- The services contract is leaking. We are leaking internal IDS. We should provide a better abstraction for the cappuccino. For instance, we should not require two IDs; we should require just one.
We need to rely on principles to scale and do things right. Good abstractions can only be created with time and practice; just because people do bad ones does not mean we should give up. Doing whatever and not paying attention to details has a price, too.
Principles of Software Architecture Modernization is full of examples, principles, and techniques on how to deal with monoliths and distributed monoliths at Scale. Continuous Modernization covers the mindset, practices, and shift to better work data with teams dealing with such systems.
Cheers,
Diego Pacheco