What are the tradeoffs between robust functionality, flexibility, simplicity, and ease of operation? How do our Armchair Architects design solutions that start as high as they want and go as low as they need? Uli, Eric, and David discuss these questions and more in a recent episode of the Azure Enablement Show.
What role does simplicity play when architecting solutions?
Simplicity is like a philosophy or a pervasive thread that you often have to revisit at every stage. From an architecture perspective you can visualize the way these solutions might fit together and then you ask yourself whether it's as simple as it needs to be or is it overly complex or can it be reduced further. Ask yourself if you are providing too many knobs and bells and whistles. Can I create a more coarse-grained API interface across my microservice architecture and that way I can make it easier on the people that have to consume it? It’s something that you visit and revisit critically all throughout the architect journey.
Simplicity is a question that you should ask from multiple dimensions. You have to also look at what happens if stuff breaks at 3am and the developer isn't available to debug the code. So how do you tell an operations person who has to deal with the system, and the more complex the system is the more difficult it is to bring it back online, or make sure the customers can successfully complete the operations. And if I have to update the solution, how many things do I have to touch to update the capabilities that I’m building. The more things I have to touch the more complex it is and therefore the likelihood of failure is much higher than if I keep it simple.
Is it possible to retain simplicity when adding features?
It’s like a scale or a spectrum. Almost an imaginary slider and if you slide it right into the more fine-grained approach, with higher technical complexity but more control for consumers of that particular service or application over capability. If you slide it the other way you get a much better user experience that's more tightly integrated, but you give up some of those exposed control capabilities and things that many developers might want. If you're an ISV you probably really do want to think about what your API surface is going to look like for consumers of that particular service.
You have to think about the way in which your consumers, your customers, or your organization will consume your components. Think about what kind of code they will have to write, that you're forcing them to write, based on the API service and the solution architecture that you're building versus the code that they want to write. In other words, is your API a delight to consume or is it really tough, because you have to spend a ton of time stitching together solution components utilizing surgical code that makes the integration really difficult and not consumable.
Surgical code is any type of integration between components that requires a significant amount of wiring up of those components so that they can talk to each other. Everything related to identity and access management, to coarse-grained or fine-grained API surface capabilities. The more powerful an individual components API is, the more you're going to have to do to get that API to talk to another API or talk to your service.
Does new functionality require more complexity?
More functionality does not automatically mean more complexity. It can potentially mean more surface area from an API perspective. Let's define what an API is these days. On the one side you have a REST interface that most likely follows OpenAPI standards from a shape perspective and exposure perspective. What used to be called Swagger, although that's now called OpenAPI, and that's certainly one API surface that you want to think about. More APIs means a little bit more complexity, but because the API follows a certain standard OpenAPI, it's fairly straightforward because it's the same shape.
The second type of API that's becoming a standard is event-based exposure so you effectively have a reactive, which is the REST-based API and a proactive or observable API where the system raises an API call through technologies like Azure Event Grid or similar. Effectively somebody then says I’m interested in that event, and again that's just more event surface. But the consumption is structured and simple so from that perspective yes, it's more complicated because it's richer, but it's not more complicated because the consumption is straightforward and easy. If you have an event and the APIs have different structures, for example instead of only REST-based APIs you also throw in some older technologies, like SOAP and mix it with XML, JSON, a CVS file, then it gets complicated. Consistency is an important part of the art of simplicity.
How do architects think about simplicity vs. complexity?
One thing that we are really pushing hard on right now in the Azure world is “concept count”. Concept count means how many concepts does a person have to understand in order to use or interact with the technology. An example of a concept would be an API, an event, or a device.
If the concept count goes above five, most people opt out because it's too complicated. Ideally you have one major concept, maybe two or three, but that's about it and most people just don't have the patience or the time to really understand five or more concepts in a single solution. Complexity is almost linear. You need to look at how many concepts you can add before the solution gets too complex for people to really grasp and put their head around, and use it consistently, which is really what you want.
You can think of architecture like fractions. Your goal is to reduce them to the lowest common denominator. But you can't just go out and say this is my simple architecture: I’ve solved it, stamped it, and it's done. In many instances you have to actually show other architects that you've completed the thought and in many cases you'll actually have to show that you considered several approaches, and explain why you rejected some concepts and deliberately chose alternatives, due to simplicity or integration of design. Or, maybe you went more complex but there was a really good reason you did that.
How should architects think about creating vs. reusing?
Reusing things is the hallmark of a great architect. Looking around and seeing what's been done already, whether it's the Well-Architected Framework on our side or something that you've done or a colleague of yours has done. Beginning the conversation with what patterns, what paradigms, what architectures exist that you can reuse before you invent, is something that's important.
How can simplicity impact the ability to maintain and update a solution?
There's another dimension on simplicity versus complexity. We always assume that solutions are built and then they’re done. But we can have 30-year-old solutions that are running on mainframes that still need to be maintained and updated. Being able to update a solution, not just from a component's perspective that you depend on, but your own code requires you to keep it as simple as possible because you might not be around later to explain it.
You might have moved on from the company or the solution and someone else has to now go in and understand how it was designed. Operational and maintenance perspectives are something that are often overlooked by people that just design and build solutions, because sometimes they're not around when things go haywire or things have to be updated with new capabilities.