Who Needs Architects? – When Tactics Do Not Add Up to Strategy

Death of Wladyslaw Szujski, 1914

Going into World War I, the French army had adopted a simple doctrine, attack. Attaque à outrance; attack to excess. Courage, particularly that inspired by the Poilu‘s dashing red trousers, would carry the day. Unfortunately for several million French soldiers, the plan was flawed.

It’s easy to see why this is the case in the age of machine guns and modern artillery, but it would likely have been just as ineffective without that complication. The bigger problem was that, even if the attacks had succeeded, each additional success would carry the seed of a greater defeat in the future. Each success would carry the victorious unit farther forward, making re-supply and communications that much more difficult. Each success would spread the victor that much thinner, making it more brittle to counter-attack.

Without focus, lots of little tactical successes can breed strategic defeat.

Or, as Richard Sage put it:

Lack of clarity should always be a warning sign.

It’s important to understand that while there is a relationship between goals, strategies, and tactics, there are differences as well. Seth Godin’s “Goals, strategy and tactics for change” captures both:

The Goal: Who are you trying to change? What observable actions will let you know you’ve succeeded?
The Strategy: What are the emotions you can amplify, the connections you can make that will cause someone to do something they’ve hesitated to do in the past (change)? The strategy isn’t the point, it’s the lever that helps you cause the change you seek.
The Tactics: What are the actions you take that cause the strategy to work? What are the events and interactions that, when taken together, comprise your strategy?

While he was defining the terms in the context of implementing change, the same definitions work well for software development. More than just a bag of tactics, a strategy is a set of tactics focused on attaining a goal. Just as a book is made up of words and sentences, it is also more than that. The intentional structuring of those words and sentences are what makes it a book; that is what gives it meaning. Likewise, systems are more than the sum of their parts.

A system exists via the structure and interaction of its component parts. A well designed system is one where those parts are intentionally structured so that their interaction achieves a goal effectively and efficiently. A failure to do so can impact the technical architecture of a system (taken from Tony DaSilva’s “Fragmented, Half-baked, Thoughts”):

System in Context by Tony DaSilva

While both System A and System B have been made to fit their context, is there any question as to which is more likely to have quality issues? Extensive refactoring, particularly at the architectural level, can become expensive. This leaves the owners of System B with a dilemma – how long can they tolerate the technical debt caused by the mismatch between the system and its context before the costs justify the work?

Failure to intentionally design can also manifest in the user experience. As Stephen Anderson tweeted:

As I noted in “Who Needs Architects? – Navigating the Fractals”, systems exist in contexts which exist in ecosystems. When designing for a particular level of abstraction, you must consider your context(s) and design your internal architecture accordingly. Likewise, you must consider the ecosystem in which the system will exist and design the external architecture accordingly. Avoiding intentional design (which I will note is neither BDUF nor over-engineering) might seem agile, but is in reality, a form of technical debt affecting quality, cohesion, and maintainability.

Tactical excellence focused on a goal is strategic excellence. Without the focus, however, tactical excellence is wasted.

“Design? Security and Privacy? YAGNI” on Iasa Global

Two of my favorite “bumper sticker philosophies” (i.e. short, pithy, and incredibly simplistic sayings) are “the simplest thing that could possibly work” and YAGNI. Avoiding unnecessary complexity and unneeded features are good ideas at first glance. The problem is determining what is unnecessary and unneeded. Just meeting the functional requirements is unlikely to be sufficient.

Read “Design? Security and Privacy? YAGNI” on the Iasa Global site for a post about how it’s important to have someone responsible for Quality of Service requirements in general and security in particular.

Form Follows Function on SPaMCAST 315

SPaMCAST logo

Have you ever wanted to put a voice with the words that you’re reading here? Now you have the chance.

Tom Cagley, who publishes one of my favorite blogs, Software Process and Measurement (SPaMCAST), has invited me to become a regular on his podcast. We decided to name the segment “Form Follows Function” (after all, what else would we call it?). We kick it off this month with a brief discussion of my post “Quick Fixes That Last a Lifetime”.

You can listen to the podcast here.

I first appeared on Tom’s “SPaMCAST 268 – Gene Hughson, Architecture, Management, Software Development” last December and I’m honored to be invited to be a regular part of his excellent podcast.

Quick Fixes That Last a Lifetime

Move Fast and Break Things on xkcd

“Move fast and break things.”

“Fail fast.”

“YAGNI.”

“Go with the simplest thing that can possibly work.”

I’ve written previously about my dislike for simplistic sound-bite slogans. Ideas that have real merit under the appropriate circumstances can be deadly when stripped of context and touted as universal truths. As Tom Graves noted in his recent post “Fail, to learn”, it’s not about failing, it’s about learning. We can’t laugh at cargo cultists building faux airports to lure the planes back while we latch on to naive formulas for success in complex undertakings without a clue as to how they’re supposed to work.

The concepts of emergent design and emergent architecture are cases in point. Some people contend that if you do the simplest thing that could possibly work, “The architecture follows immediately from that: the architecture is just the accumulation of these small steps”. It is trivially true that an architecture will emerge under those circumstances. What is unclear (and unexplained) is how a coherent architecture is supposed to emerge without any consideration for the higher levels of scope. Perhaps the intent is to replicate Darwinian evolution. If so, that would seem to ignore the fact that Darwinian evolution occurs over very long time periods and leaves a multitude of bodies in its wake. While the species (at least those that survive) ultimately benefit, individuals may find the process harsh. If the fittest (most adaptable, actually) survive, that leaves a bleaker future for those that are less so. Tipping the scales by designing for more than the moment seems prudent.

Distributed systems find it even more difficult to evolve. Within the boundary of a single application, moving fast and breaking things may not be fatal (systems dealing with health, safety, or finance are likely to be less tolerant than social networks and games). With enough agility, unfavorable mutations within an application can be responded to and remediated relatively quickly. Ill-considered design decisions that cross system boundaries can become permanent problems when cost and complexity outweigh the benefits of fixing them. There is a great deal of speculation that the naming of Windows 10 was driven by the number of potential issues that would be created by naming it Windows 9. Allegedly, Microsoft based its decision on not triggering issues caused by short-sighted decisions on the part of developers external to Microsoft. As John Cook noted:

Many think this is stupid. They say that Microsoft should call the next version Windows 9, and if somebody’s dumb code breaks, it’s their own fault.

People who think that way aren’t billionaires. Microsoft got where it is, in part, because they have enough business savvy to take responsibility for problems that are not their fault but that would be perceived as being their fault.

It is naive, particularly with distributed applications, to act as if there are no constraints. Refactoring is not free, and consumers of published interfaces create inertia. While it would be both expensive and ultimately futile to design for every circumstance, no matter how improbable, it is foolish to ignore foreseeable issues and allow a weakness to become a “standard”. There is a wide variance between over-engineering/gold-plating (e.g. planting land mines in my front yard just in case I get attacked by terrorists) and slavish adherence to a slogan (e.g. waiting to install locks on my front door until I’ve had something stolen because YAGNI).

I can move fast and break things by wearing a blindfold while driving, but that’s not going to get me anywhere, will it?

Microservices Under the Microscope

Microscope

Buzz and backlash seems to describe the technology circle of life. Something (language, process, platform, etc.; it doesn’t seem to matter) gets noticed, interest increases, then the reaction sets in. This was seen with Service-Oriented Architecture (SOA) in the early 2000s. More was promised than could ever be realistically attained and eventually the hype collapsed under its own weight (with a little help from the economic downturn). While the term SOA has died out, services, being useful, have remained.

2014 appears to be the year of microservices. While neither the term nor the architectural style itself are new, James Lewis and Martin Fowler’s post from earlier this year appears to have significantly raised the level of interest in it. In response to the enthusiasm, others have pointed out that the microservices architectural style, like any technique, involves trade offs. Michael Feathers pointed out in “Microservices Until Macro Complexity”:

If services are often bigger than classes in OO, then the next thing to look for is a level above microservices that provides a more abstract view of an architecture. People are struggling with that right now and it was foreseeable. Along with that concern, we have the general issue of dealing with asynchrony and communication patterns between services. I strongly believe that there is a law of conservation of complexity in software. When we break up big things into small pieces we invariably push the complexity to their interaction.

Robert, “Uncle Bob”, Martin has recently been a prominent voice questioning the silver bullet status of microservices. In “Microservices and Jars”, he pointed out that applications can achieve separation of concerns via componentization (using jars/Gems/DLLs depending on the platform) without incurring the overhead of over-the-wire communication. According to Uncle Bob, by using a plugin scheme, components can be as independently deployable as a microservice.

Giorgio Sironi responded with the post “Microservices are not Jars”. In it, Giorgio pointed out independent deployment is only part of the equation, independent scalability is possible via microservices but not via plugins. Giorgio questioned the safety of swapping out libraries, but I can vouch for the fact that plugins can be hot-swapped at runtime. One important point made was in regard to this quote from Uncle Bob’s post:

If I want two jars to get into a rapid chat with each other, I can. But I don’t dare do that with a MS because the communication time will kill me.

Sironi’s response:

Of course, chatty fine-grained interfaces are not a microservices trait. I prefer accept a Command, emit Events as an integration style. After all, microservices can become dangerous if integrated with purely synchronous calls so the kind of interfaces they expose to each other is necessarily different from the one of objects that work in the same process. This is a property of every distributed system, as we know from 1996.

Remember this for later.

Uncle Bob’s follow-up post, “Clean Micro-service Architecture”, concentrated on scalability. It made the point that microservices are not the only method for scaling an application (true); and stated that “the deployment model is a detail” and “details are never part of an architecture” (not true, at least in my opinion and that of others):

While Uncle Bob may consider the idea of designing for distribution to be “BDUF Baloney”, that’s wrong. That’s not only wrong, but he knows it’s wrong – see his quote above re: “a rapid chat”. In the paper that’s referenced in the Sironi quote above, Waldo et al. put it this way:

We argue that objects that interact in a distributed system need to be dealt with in ways that are intrinsically different from objects that interact in a single address space. These differences are required because distributed systems require that the programmer be aware of latency, have a different model of memory access, and take into account issues of concurrency and partial failure.

You can design a system with components that can run in the same process, across multiple processes, and across multiple machines. To do so, however, you must design them as if they were going to be distributed from the start. If you begin chatty, you will find yourself jumping through hoops to adapt to a coarse-grained interface later. If you start with the assumption of synchronous and/or reliable communications, you may well find a lot of obstacles when you need to change to a model that lacks one or both of those qualities. I’ve seen systems that work reasonably well on a single machine (excluding the database server) fall over when someone attempts to load balance them because of a failure to take scaling into account. Things like invalidating and refreshing caches as well as event publication become much more complex starting with node number two if a “simplest thing that can work” approach is taken.

Distributed applications in general and microservice architectures in particular are not universal solutions. There are costs as well as benefits to every architectural style and sometimes having everything in-process is the right answer for a given point in time. On the other hand, you can’t expect to scale easily if you haven’t taken scalability into consideration previously.

“Design By Committee” on Iasa Global Blog

Who's driving?

Can a team of experienced, empowered developers successfully design the architecture of a product? Sure.

Can a team of experienced, empowered developers successfully design the architecture of a product without a unified vision of how the architecture should be structured to accomplish its purpose? Probably not.

Can a team of experienced, empowered developers successfully design the architecture of a product while staying within the bounds of their role as developers? Absolutely not.

See the full post on the Iasa Global Blog (a re-post, originally published here).

Accidental Architecture

Hillside Slum

I’m not sure if it’s ironic or fitting that my very first post on Form Follows Function, “Like it or not, you have an architecture (in fact, you may have several)”, dealt with the concept of accidental architecture. A blog dedicated to software and solution architecture starts off by discussing the fact that architecture exists even in the absence of intentional design? It is, however, a theme that seems to recur.

The latest recurrence was a Twitter exchange with Ruth Malan, in which she stated:

Design is the act and the outcome. We design a system. The system has a design.

This prompted Arnon Rotem-Gal-Oz to observe that architecture need not be intentional and “…even areas you neglect well [sic] have design and then you’d have to deal with its implications”. To this I added “accidental architecture is still architecture – whether it’s good architecture or not is another thing”.

Ruth closed with a reference to a passage by Grady Booch:

Every software-intensive system has an architecture. In some cases that architecture is intentional, while in others it is accidental. Most of the time it is both, born of the consequences of a myriad of design decisions made by its architects and its developers over the lifetime of a system, from its inception through its evolution.

The idea that an architecture can “emerge” out of skillful construction rather than as a result of purposeful design, is trivially true. The “Big Ball of Mud”, an ad hoc arrangement of code that grows organically, remains a popular design pattern (yes, it’s a pattern rather than an anti-pattern – see the Introduction of “Big Ball of Mud” for an explanation of why). What remains in question is how effective is an architecture that largely or even entirely “emerges”.

Even the current architectural style of the day, microservices, can fall prey to the Big Ball of Mud syndrome. A plethora of small service applications developed without a unifying vision of how they will make up a coherent whole can easily turn muddy (if not already born muddy). The tagline of Simon Brown’s “Distributed big balls of mud” sums it up: “If you can’t build a monolith, what makes you think microservices are the answer?”.

Someone building a house using this theory might purchase the finest of building materials and fixtures. They might construct and finish each room with the greatest of care. If, however, the bathroom is built opening into the dining room and kitchen, some might question the design. Software, solution, and even enterprise IT architectures exist as systems of systems. The execution of a system’s components is extremely important, but you cannot ignore the context of the larger ecosystem in which those components will exist.

Too much design up front, architects attempting to make decisions below the level of granularity for which they have sufficient information, is obviously wrong. It’s like attempting to drive while blindfolded using only a GPS. By the same token, jumping in the car and driving without any idea of a destination beyond what’s at the end of your hood is unlikely to be successful either. Finding a workable balance between the two seems to be the optimal solution.

[Shanty Town Image by Otsogey via Wikimedia Commons.]

Architecture – Finding Simple Solutions Over a Lifetime of Problems

On Roger Sessions’ LinkedIn group, Simpler IT, the discussion “What do I mean by Simplifying” talks about finding simple solutions to problems. Roger’s premise is that every problem has its own inherent complexity:

Let’s say P is some problem that we need to solve. For example, P could be the earthquake in Tom’s example or P could be the need of a bank to process credit cards or P could be my car that needs its oil changed. P may range in complexity from low (my car needs its oil changed) to high (a devastating earthquake has occurred.)

For a given P, the complexity of P is a constant. There is no strategy that will change the complexity of P.

Complexity and Effectiveness

Roger goes on to say that for any given problem, there will be a set of solutions to that problem. He further states “…if P is non-trivial, then the cardinality of the solution set of P is very very large”. Each solution can be characterized by how well it solves the problem at hand and how complex the solution is. These attributes can be graphed, as in the image to the right, yielding quadrants that range from the most effective and least complex (best) to least effective and most complex (worst). Thus, simplifying means:

The best possible s in the solution set is the one that lives in the upper left corner of the graph, as high on the Y axis as possible and as low on the X axis as possible.

When I talk about simplifying, I am talking about finding that one specific s out of all the possible solutions in the solution set.

Simplification, as a strategy, makes a great deal of sense in my opinion. There is, however, another aspect to be considered. While the complexity of a given problem P is constant, P represents the problem space of a system at a given time, not the entire lifecycle. The lifecycle of a system will consist of a set of problem spaces over time, from first release to decommissioning. An architect must take this lifecycle into consideration or risk introducing an ill-considered constraint on the future direction of the product. This is complicated by the fact that there will be uncertainty in how the problem space evolves over time, with the uncertainty being the greatest at the point furthest from the present (as represented by the image below).

product timeline

Some information regarding the transition from one problem space to the next will be available. Product roadmaps and deferred issues provide some insight into what will be needed next. That being said, emergent circumstances (everything from unforeseen changes in business direction to unexpected increases in traffic) will conspire to prevent the trajectory of the set of problem spaces from being completely predictable.

Excessive complexity will certainly constrain the options for evolving a system. However, flexibility can come with a certain amount of complexity as well. The simplest solution may also complicate the evolution of a system.

The Never-Ending Narrative of Architecture

Weaving a web

All of my creation is an effort to weave a web of connection with the world: I am always weaving it because it was once broken.

Anaïs Nin by way of @RuffyanMe

When is an architect’s job done? Is it upon completion of the “design phase”? Is it when the project is complete?

In my opinion, the best definition of the architectural process (see slide 16) is Tom Gilb’s: “A continuous, and lifecycle long, activity of finding means for ends”. The activities facilitated by the application are unlikely to be static, so it is unrealistic to expect it to remain the same. Even where the business doesn’t change significantly, our understanding of it may:

I bet the users are very grateful at first, but then they come to rely on it. And, what’s more, they like the system less and less over the course of time. Every time the USPS changes the price of postage you have to go into this system and made changes, and, what’s worse is that the part that generates the documents seems to break every time there’s a new version or even an update to Word.

Erik Dietrick, “Beware Mindless Automation”

Even if a particular business process was unchanging and its automation was executed perfectly, the platform on which it runs will not. Failure to plan for platform evolution is planning to fail due to lack of maintenance.

In short, an application is a relationship. Unless you plan to never see its users again, it’s a long-term commitment, not just a project. So long as the application exists, there is a need for the architect role to provide advice on the evolution of the product and its platform. Without this continuity of vision, the result is likely to be the “Shantytown” pattern described by Foote and Yoder in “Big Ball of Mud”:

All too many of our software systems are, architecturally, little more than shantytowns. Investment in tools and infrastructure is too often inadequate. Tools are usually primitive, and infrastructure such as libraries and frameworks, is undercapitalized. Individual portions of the system grow unchecked, and the lack of infrastructure and architecture allows problems in one part of the system to erode and pollute adjacent portions.

If the architectural process can be described a narrative, it should also be seen as a story consisting of several component stories, carrying the product from cradle to grave(*). Maximizing cohesion within a given narrative (release) is important. Likewise, benefits will accrue from maintaining continuity from one narrative to the next.

The connection between the application and the ends it serves is seldom static. The longer the web is left unattended, the more likely it is to break. Much can slip through a broken web, and each escape widens the breach.

Maintenance can seem costly without the perspective of the cost of replacement, not to mention value lost to obsolescence.

* It’s not exactly “never-ending”, but I like alliteration, so the title stands. 😉

Design by Committee

Who's driving?

Can a team of experienced, empowered developers successfully design the architecture of a product? Sure.

Can a team of experienced, empowered developers successfully design the architecture of a product without a unified vision of how the architecture should be structured to accomplish its purpose? Probably not.

Can a team of experienced, empowered developers successfully design the architecture of a product while staying within the bounds of their role as developers? Absolutely not.

A lot of ink, both physical and virtual, has been spilled arguing over the utility of architecture and architects. A common misunderstanding among the anti-architect faction is that an application is the sum of its parts. This is particularly prevalent with those espousing the view that if the team does the “simplest thing that could possibly work”, the architecture will just emerge. To be honest, an architecture will emerge from this method. Whether that architecture is coherent, performant, scalable, secure, etc. is quite another matter.

In order to have an informed opinion on whether or not a particular role is need, it is necessary to understand the nature of that role. For example, while the ability to code is a critical qualification for both application and solution architects, it is not the sole qualification. While architects are concerned with the structure of the code, that structure is a means to meet the needs of a customer, not the end in itself.

The purpose of an architect is to understand the needs of the stakeholders and meld his/her knowledge of code, platform (OS, database and web server, etc.), and environment (machines and network) into a cohesive whole that accomplishes the mission. This means the architect’s focus must be on the product as whole, not just a particular project or release and also not on just one aspect (code) of the product. The evolution of both code and platform (hardware, supporting software, and network) needs to be managed across the lifecycle of the product in order for it to remain useful.

For smaller products and teams, it may well be possible for the team to undertake the tasks listed above in addition to their duties as developers. As the scale of the product grows, however, dealing with multiple levels of abstraction and achieving timely consensus across multiple feature teams becomes problematic. At this point, coordination and specialization becomes more important. The architect role then becomes responsible for collaboratively developing and maintaining a unified high-level vision, providing sufficient guidance to keep the product coherent as it evolves while avoiding the trap of Big Design Up Front.

Whether the role is fulfilled by an individual or multiple individuals or the entire team as whole is less important than whether the role is handled effectively. Both “Whiteboard Architecture” and the classic “Big Ball of Mud” need to be avoided.