I was arguing for small composable services before 2010, well before the word “microservices” existed. The technical reason was only half of the story.
Back around maybe 2007, I was telling teams to stop writing big applications, to break things down into smaller constituent elements. Create small, focused, finished pieces, and then you “snap” these together into workflows and applications. You would build business processes from a handful of services that each did one thing well.
Nobody called this “microservices” yet, the term had yet to be coined. It would be several years before the industry agreed on a name, started throwing it onto conference slides, creating momentum behind a pattern that would drive better results. I wasn’t fabulous at branding my ideas back then. Let’s not even talk about the Agile-style architecture and delivery approach I was doing earlier.
Anyway, the point of adding the composability angle came from correcting gaps in projects where I designed the architecture, and delivery teams had to make numerous changes prior to launch, and were pressed for time. Part of the reason for this was fixing bugs, but the other part was another pattern I saw.
It was an assumption that the long requirements document that took months to write was absolutely correct, understood the voice of the customer, and was spot on in terms of capturing what we needed the application “to be”. My experience had been more realistic, much more pragmatic, and knowing the target likely needed to change. It’s really hard to be completely right up front. We can’t see and experience what the application needs to be until we see it and play with it.
Back to the amount of change nearing launch. I was well aware of the inherent risk of changes introduced late, the brittleness of the code, and high probability of not adequately testing for important edge cases.
Being right early has its downside. You’re immediately forced into conversations and breaking the gravitational pull of the status quo. Most of the pushback I got wasn’t really about the architecture, but was more about control, politics, ownership, and the idea there was some shared codebase that everyone could call “ours.” Smaller services seemed confusing to many people, and I took the heat for this. This centered around the ideas of more frequent deployments, being more complicated, not “fitting” into the service catalog we used at the time. I argued this approach would lead to better results instead of deploying larger, unwieldy blobs of code, and I was able to get buy in for this.
The small code argument was only half the story. The other half is the part before the DevOps movement started, but an idea that resonated with me the first time I heard it, the “you build it, you run it” concept, which made complete and immediate sense to me. Reduce unnecessary handoffs and long conversations, simplify. I made my staff support the services they built. The rule was that if you wrote it, you ran it, you supported it, but also you had to explain what you were learning from the support that didn’t factor into your original design. This is where I was able to evaluate their growth and potential.
Operator understanding is essential for good design
You only really learn, really grok what a good interface looks like after you’ve experienced the fallout from a bad one. When I say fallout, I mean you have to feel it firsthand — you get the call, you have to fix it, the system might be down, and you have to do it fast, under pressure. You have to deliver. I noticed early in managing teams that the ability to design a good interface wasn’t every developer’s forte.
An engineer who never gets called and does not answer a hard integration question does not gain the experience to learn what “good” means. Back to the “throwing code over the wall” idea, not satisfactorily documenting it, commenting it, describing articulately what it did, how it worked, and a few other choice terms that resonated. I talked to many people on the support teams of my organization, and concurred. They were absolutely right. There were long meetings, lots of work, where the development teams were required to explain their applications, services, and code, yet still substantial gaps and room for less than desired behavior came to fruition, in production.
I watched engineers who had been coasting for years have to sharpen up within two months of owning their code end-to-end. The APIs got simpler, error messages got more straightforward and interpretable. Clearer, more meaningful documentation was created. The arguments about what a “clean” interface looked like quietly stopped, because the person who had to answer the phone was already getting the picture.
We keep seeing the same pattern
What I noticed over years of doing this is that the dynamic I saw on my own teams was not specific to my teams. The same pattern plays out at a larger, industry scale, and has been doing so for decades.
In the early 2000s it was service-oriented architecture. Then in the mid-2010s, microservices was being seen as a panacea (to an extent). Cut ahead to 2020, and we were thinking serverless and event-driven was the future. Now, we’re more focused on internal developer platforms, tools, and developer experience. Each wave has a very similar promise. Break code into smaller, composable units, then assemble them into workflows modeling real business processes, and iterating with a short feedback loop creates better outcomes.
The challenges every wave runs into are the same types of failure when an organization skips the rules for support. It’s near impossible to build a good internal platform if the platform team has never been the consumer of one. You cannot know what a good interface or good abstraction looks like without getting burned by a bad one, and then being on the hook to fix it. Feeling the pain of having to fix stays with developers. We’ve seen the technology and targets change, yet the rule stays the same.
Adhere to these simple principles
If I were writing basic architecture guidance for a company today, it would largely be the same few lines I was saying back then.
- Keep the pieces small
- Run what you build
- As systems age, keep reviewing and observant on when things are getting complicated
Keep these in mind and you’ll see better delivery.