Microservices or Monoliths – Fences and Neighbors

Photo of fence separating fields from a road

 

At the end of my last post, “What Makes a Monolith Monolithic?”, I stated that I didn’t consider the term “monolithic” to be inherently derogatory. It is, rather, a descriptive term relating to the style of organizing an application’s architecture. Depending on the context the system operates within, a monolithic architectural style could lie anywhere on the continuum between perfectly suited and perfectly disastrous. Placing it on that continuum requires a sense of what qualities are most needed or desired and which can be traded off in their stead. Everything comes with a cost, and attempting to ignore that fact merely sets us up for unpleasant future surprises.

After an initial period of unbridled enthusiasm, opinion seemed to gel around the idea that highly distributed application architectures (aka microservice architectures) were not suitable to all contexts. There are prerequisites for jumping into the microservices pool in terms of problem architecture, infrastructure, and organization. Attempting to shoehorn a microservice architecture into an environment that cannot support it will be overly expensive at best and a failure of apocalyptic proportions at worst.

There are many aspects of application design that are commonly recognized as beneficial: modularity, loose-coupling, high cohesion, and separation of concerns. It is critical to realize that these aspects can be found in systems with microservice architectures, monolithic systems, and everything in between. Distributed architectures are not necessary for modularity, nor any of those other aspects. In fact, one could easily create an application with a microservice architecture whose qualities are opposite to these desirable ones.

There are, however, situations where the benefit of a microservice architecture outweighs the costs and complexity. The ability to independently deploy and scale the various parts of an application is a major benefit, in my opinion. A well designed microservice architecture can even allow for the components of an application to be replaced on the fly. These features are not unique to microservice architectures, but are arguably easier to achieve than in other application architectures.

Real design, balancing both costs and benefits, is required. Sticking a bit of network in between the components is insufficient to ensure success. Deliberate design, especially as the boundaries multiply, is critical for an effective system. Identifying and providing for those boundaries at the conceptual level (i.e. before they become physical) is key. Good fences can either make for good neighbors, or they can create a maze of barriers.

Advertisement

Design for Life

Soundview, Bronx, NY

 

The underlying theme of my last post, “Babies, Bathwater, and Software Architects”, was that it’s necessary to understand the role of a software architect in order to understand the need for that role. If our understanding of the role is flawed, not just missing aspects of what the role should be focusing on, but also largely consisting of things the role should not be concerned with, then we can’t really effectively determine whether the role is needed or not. If a person drowning is told that an anvil is a life-preserver, that doesn’t mean that they don’t need a life-preserver. It does mean they need a better definition of “life-preserver”.

Ruth Malan, answering the question “Do we still need architects?”, captures the essence of what is unique about the concerns of the software architect role:

There’s paying attention to the structural integrity of the code when that competes with the urge to deliver value and all we’ve been able to accomplish in terms of the responsiveness of continuous integration and deployment. We don’t intend to let the code devolve as we respond to unfolding demands, but we have to intend not to, and that takes time — possibly time away from the next increment of user-perceived value. There’s watching for architecturally impactful, structurally and strategically significant decisions, and making sure they get the attention, reflection, expertise they require. Even when the team periodically takes stock, and spends time reflecting learning back into the design/code, the architect’s role is to facilitate, to nurture, the design integrity of the system as a system – as something coherent. Where coherence is about not just fit and function, but system properties. Non-trivial, mutually interacting properties that entail tradeoffs. Moreover, this coherence must be achieved across (microservice, or whatever, focused) teams. This takes technical know-how and know-when, and know-who to work with, to bring needed expertise to bear.

These system properties are crucial, because without a cohesive set of system properties (aka quality of service requirements), the quality of the system suffers:

Even if the parts are perfectly implemented and fly in perfect formation, the quality is still lacking.

A tweet from Charles T. Betz points out the missing ingredient:

Now, even though Frederick Brooks was writing about the architecture of hardware, and the nature of users has drastically evolved over the last fifty-four years, his point remains: “Architecture must include engineering considerations, so that the design will be economica1 and feasible; but the emphasis in architecture is upon the needs of the user, whereas in engineering the emphasis is upon the needs of the fabricator.”

In other words, habitability, the quality of being “livable”, is a critical condition for an architecture. Two systems providing the exact same functionality may not be equivalent. The system providing the “better” (from the user’s perspective) quality of service will most likely be seen as the superior system. In some cases, quality of service concerns can even outweigh functional concerns.

Who, if not the software architect, is looking out for the livability of your system as a whole?

“Microservice Mistakes – Complexity as a Service” on Iasa Global

I’m pleased to announce that I’ve been asked to continue as a contributor to the Iasa Global site. I’m planning to post original content there on at least a monthly basis. In the interim, please enjoy a re-post of “Microservice Mistakes – Complexity as a Service”

Law of Unintended Consequences – Security Edition

Bank Vault

More isn’t always better. When it comes to security, more can even be worse.

As the use of encryption has increased, management of encryption keys has emerged as a pain point for many organizations. The amount of encrypted data passing through corporate firewalls, which has doubled over the last year, poses a severe challenge to security professionals responsible for protecting corporate data. The mechanism that’s intended to protect information in transit does so regardless of whether the transmission is legitimate or not.

Greater complexity, which means greater inconvenience, can lead to decreased security. Usability increases security by increasing compliance. Alarm fatigue means that as the number of warnings increase, so does the likelihood of their being ignored

Like any design issue, security should be approached from a systems thinking viewpoint (at least in my opinion). Rather than a one-dimensional, naive approach, a holistic one that recognizes and deals with the interrelationships is more likely to get it right. Thinking solely in terms of actions while ignoring the reactions that result from them hampers effective decision-making.

To be effective, security should be comprehensive, coordinated, collaborative, and contextual.

Comprehensive security is security that involves the entire range of security concerns: application, network, platform (OS, etc.), and physical. Strength in one or more of these areas means little if only one of the others is fatally compromised. Coordination of the efforts of those responsible for these aspects is essential to ensure that the various security enhance rather than hinder security. This coordination is better achieved via a collaborative process that reconciles the costs and benefits systemically than a prescriptive one imposed without regard to those factors. Lastly, practices should be tailored to the context of the problem at hand. Value at risk and amount of exposure are two factors that should help determine the effort expended. Putting a bank vault door on the garden shed not only wastes money, but also hinders security by taking those resources away from an area of greater need.

As with most quality of service concerns, security is not a binary toggle but a continuum. Matching the response to the need is a good way to stay on the right side of the law of unintended consequences.

Microservice Mistakes – Complexity as a Service

Michael Feathers’ tweet about technical empathy packs a lot of wisdom into 140 characters. Lack of technical empathy can lead to a system that is harder to both implement and maintain since no thought was given to simplifying things for the caller. Maintainability is one of those quality of service requirements that appears to be a purely technical consideration right up to the point that it begins significantly affecting development time. If this sounds suspiciously like technical debt to you, then move to the head of the class.

The issue of technical empathy is particularly on point given the popular interest in microservice architectures (MSAs). The granular nature of the MSA style brings many benefits, but also comes with the cost of increased complexity (among others). Michael Feathers referred to this in a post from last summer titled “Microservices Until Macro Complexity”:

It is going to be interesting to see how this approach scales. Some organizations have a relatively low number of microservices. Others are pushing higher, around the 600 mark. This is a bit beyond the point where people start seeking a bigger picture. 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.

The last sentence bears repeating: “When we break up big things into small pieces we invariably push the complexity to their interaction”. Breaking a monolith into microservices simplifies each individual component, however, as Eran Hammer observed in “The Fallacy of Tiny Modules”, “…at some point, someone has to put it all together and then, all the complexity is going to surface…”. As Yamen Sader illustrated in his slide deck “Microservices 101 – The Big Why” (slide #26), the structure of the organization, domain, and system will diverge in an MSA. The implication of this is that for a given domain task, we need to know more about the details of that task (i.e. have less encapsulation of the internals) in a microservice environment.

The further removed the consumer of these services are from the providers, the harder and less convenient it will be to transfer that knowledge. To put this in perspective, consider two fast food restaurants: one operates in a traditional manner where money is exchanged for burgers, the other is a microservice style operation where you must obtain the beef, lettuce, pickles, onion, cheese, and buns separately, after which the cooking service combines them (provided the money is there also). The second operation will likely be in business for a much shorter period of time in spite of the incredible flexibility it offers. Additionally, it that flexibility only truly exists when the contracts between two or more providers are swappable. As Ben Morris noted in “Do Microservices create extra challenges for distributed development?”:

Each team will develop its own interpretation of the system and view of the data model. Any service collaboration will have to involve some element of translation or mapping and will be vulnerable to subtle bugs that can arise from semantic differences.

Adopting a canonical model across the platform can help to address this problem by asserting a common vocabulary. Each service is expected to exchange information based on a shared definition of the main entities. However, this requires a level of cross-team governance which is very much at odds with the decentralised nature of microservices.

None of this is meant to say that the microservice architecture style is “bad” or “wrong”. It is my opinion, however, that MSA is first and foremost an architectural style of building applications rather than systems of systems. This isn’t an absolute; I could see multiple MSA applications within an organization sharing component microservices. Where those microservices transcend the organizational boundary, however, making use of them becomes more complex. Actions at a higher level of granularity, such as placing an order for some product or service, involves coordinating multiple services in the MSA realm rather than consuming one “chunkier” service. While the principles behind microservice architectures are relevant at higher levels of abstraction, a mismatch in granularity between the concept and implementation can be very troublesome. Maintainability issues are both technical debt and a source of customer dissatisfaction. External customers are far easier to lose than internal ones.

“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.

“Beware Premature Certainty – Embracing Ambiguous Requirements” on Iasa Global Blog

You have to measure it to manage it!

Many see ambiguity as antithetical to software requirements. However, as Ruth Malan has observed:

Pick your battles with ambiguity careful. She is a wily foe. Not to be dominated. Rather invited to reveal the clarities we can act on. Make decisions with the understanding that we need to watchful, for our assumptions will, sooner or later, become again tenuous in that fog of ambiguity and uncertainty that change churns up.

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

Beware Premature Certainty – Embracing Ambiguous Requirements

You have to measure it to manage it!

Many see ambiguity as antithetical to software requirements. However, as Ruth Malan has observed:

Pick your battles with ambiguity careful. She is a wily foe. Not to be dominated. Rather invited to reveal the clarities we can act on. Make decisions with the understanding that we need to watchful, for our assumptions will, sooner or later, become again tenuous in that fog of ambiguity and uncertainty that change churns up.

Premature certainty, locking in too soon to a particular quality of service metric or approach to a functional requirement can cause as many problems as requirements that are too vague. For quality of service, also known as “non-functional”, requirements, this manifests as metrics that lack a basis in reality, metrics that do not account for differing circumstances, and/or metrics that fail to align with their objective. For functional requirements, premature certainty presents as designs masquerading as requirements (specifying “how” instead of “what”) and/or contradictory requirements. For both functional and quality of service requirements, this turns what should be an aid to understanding into an impediment, as well as a source of conflict.

Quality of service requirements are particularly susceptible to this problem. In order to manage quality, metrics must be defined to measure against. Unfortunately, validity of those metrics is not always a priority. Numbers may be pulled from thin air, or worse, marketing materials. Without an understanding of the costs, five 9s availability looks like a no-brainer.

Committing to a metric without understanding the qualitative value of that measure is risky. A three-second response time sounds quick, but will it feel quick? Is it reasonable given the work to be performed? Is it reasonable across the range of environmental conditions that can be expected? How exactly does one measure maintainability? As Tom Graves, in “Metrics for qualitative requirements” noted:

To put it at perhaps its simplest, there’s a qualitative difference between quantitative-requirements and qualitative ones: and the latter cannot and must not be reduced solely to some form of quantitative metric, else the quality that makes it ‘qualitative’ will itself be lost.

In another post, “Requisite Fuzziness”, Tom points out that measures are proxies for qualities, not the qualities themselves. A naive approach can fail to meet the intended objective: “The reality is that whilst vehicle-speed can be measured quite easily, often to a high degree of precision, ‘safe speed’ is highly-variable and highly-contextual.” This context-bound nature begets a concept he refers to as “requisite fuzziness”:

It’s sort-of related to probability, or uncertainty, but it’s not quite the same: more an indicator of how much we need to take that uncertainty into account in system-designs and system-governance. If there’s low requisite-fuzziness in the context, we can use simple metrics and true/false rules to guide decision-making for that context; but if there’s high requisite-fuzziness, any metrics must be interpreted solely as guidance, not as mandatory ‘rules’.

The benefits of requisite fuzziness for functional requirements is somewhat counter-intuitive. Most would argue against ambiguity, as indeed Jeff Sutherland did in “Enabling Specifications: The Key to Building Agile Systems”:

The lawyers pointed out that a patent application is an “enabling specification.” This is a legal term that describes a document that allows the average person knowledgeable in the domain to create the feature without having any discussion with the originators of the enabling specification.

In general, requirements are NOT enabling specifications. On a recent project at a large global company we discovered that hundreds of pages of requirements were not enabling specifications. On the average 60% of what was in the documents was useless to developers. It caused estimates to double in size. Even worse, 10% of what was needed by developers to implement the software was not in the requirements.

The enabling specifications used at PatientKeeper provided a global description of a feature set framed as lightweight user stories with screen shots, business logic, and data structures. The enabling specification was used to generate user stories which then formed the product backlog. The global feature description was updated regularly by the Product Owner team and was a reference to the state of the system that allowed developers to see where the user stories in the product backlog came from.

A user story needes to be a mini-enabling specification for agile teams to operate at peak performance. If it is not, there will be the need for continued dialogue with the Product Owner during the sprint to figure out what the story means. This will reduce story process efficiency and cripple velocity.

While this level of detail may well enable a team to develop efficiently, it may cripple the team’s ability to develop effectively. Given the interdependent relationship between architecture and requirements, overly prescriptive requirements can introduce risk. When design elements (such as “data structures” above) find their way into requirements, it may be that the requirement cannot be implemented as specified within the constraints of the architecture. Without the dialog Jeff referred to, either the requirement will be ignored or the integrity of the architecture will be violated. Neither of these options are advisable.

Another danger inherent in this situation is that of solving the wrong problem. This was addressed by Charlie Alfred in “Invisible Requirements”:

Coming to solutions (requirements) too quickly often times overlooks potentially more beneficial solutions. To illustrate this, consider the Jefferson Memorial.

Several years ago, excessive erosion of the Jefferson Memorial was noticed. A brief investigation identified excessive cleaning as the cause. Since the memorial must be kept clean, more investigation was necessary. Bird droppings were identified as the culprit, so actions were taken to have fewer birds around.Eventually, however, someone asked why the birds were such a problem with the Jefferson Memorial and not the others. Another study determined that the birds frequented the memorial, not for love of Jefferson, but for love of the many tasty spiders that made their home there. Probing further, the spiders were thriving because the insect population was proliferating.Finally, understanding that the insects were attracted by the memorial lights at dusk and dawn identified the ultimate solution. Turn off the lights. Initial solutions driven by quick decisions by memorial managers (i.e. powerful stakeholders) provided expensive ill-suited solutions for an ill-understood problem. The root cause and final solution requirement were well hidden, only brought to light by extensive and time consuming trial and error solutions. Each required solution inappropriately framed the problem, missing the associated hidden causes and final necessary requirement.

“Expensive”, coupled with “extensive and time consuming”, should give pause, particularly when used to describe failures. Naively implementing a set of requirements without technical analysis may well harm the customer. In “Changing Requirements: You Have a Role to Play!”, Raja Bavani noted:

You have to understand what business analysts or product owners provide you. You have to ask questions as early as you can. You have to think in terms of test scenarios and test data. You have to validate your thoughts and assumptions whenever you are in doubt. You have to think about related user stories and conflicting requirements. Instead of doing all these, if you are going to remain a passive consumer of the inputs received from business analysts or product owners, I am sure you are seeding bug issues.

While we don’t want requirements that are deliberately undecipherable, neither can we expect requirements that are both fully developed and cohesive with the architecture as a whole. Rather, we should hope for something like what Robert Galen suggested, that “communicates…goals while leaving the flexibility for my architect to do their job”. They should be, according to J. B. Rainsberger, a ticket to a conversation.

Lisa Crispin captured the reason for this conversation in “Helping the Customer Stick to the Purpose of a User Story”:

Make sure you understand the *purpose* of a user story or feature. Start with the “why”. You can worry later about the “how”. The customers get to decide on the business value to be delivered. They generally aren’t qualified to dictate the technical implementation of that functionality. We, the technical team, get to decide the best way to deliver the desired feature through the software. Always ask about the business problem to be solved. Sometimes, it’s possible to implement a “solution” that doesn’t really solve the problem.

Likewise, Roman Pichler observed:

If I say, for instance, that booking a training course on our website should be quick, then that’s a first step towards describing the attribute. But it would be too vague to characterise the desired user experience, to help the development team make the right architecture choices, and to validate the constraint. I will hence have to iterate over it, which is best done together with the development team.

Rather than passively implementing specifications and hoping that a coherent architecture “emerges”, iteratively refining requirements with those responsible for the architecture stacks the deck in favor of success by surfing ambiguity:

“When you ask a question, what two word answer distinguishes the architect?” And they didn’t miss a beat, answering “It depends” in an instant. “That’s my litmus test for architects.” I told them. “So, how do I tell a good architect?”…I said “They tell you what it depends on.” Yes, “it depends” is a hat tip to multiple simultaneous possibilities and even truths, which is the hallmark of ambiguity (of the kind that shuts down those who are uncomfortable with it). The good architect can sense what the dependencies are, and figure what to resolve and what to live with, to make progress.

Ruth Malan, A Trace in the Sand

What’s driving your architecture?

In a recent post titled “Architectural Drivers”, Davy Brion listed the factors that influence his architectural choices. Additionally, he makes a number of important observations, such as the wisdom of avoiding unnecessary complexity and the fact that there is no absolute “right” or “wrong”, only trade-offs. His efforts inspired me to put together my own list of drivers, several of which are the same or at least related to his. Additionally, I include some discussion of how they affect the architecture of a given system or solution.

Functionality

Functionality being the first consideration should not come as a surprise since the name of the site is “Form Follows Function”. Are the user interactions task-oriented processes, basic CRUD operations, or a combination of both? Are those interactions real-time, asynchronous, or a mixture? Is the system stand-alone, server-based, peer to peer, or a hybrid? How much, if any, of the desired functionality is available from third party providers?

The requirements that describe the system’s behavior should be a major determinant of the solution’s application architecture, data architecture, and infrastructure architecture. It is important to look at the requirements in a holistic manner. Although those of the current iteration of the current release will be foremost in mind, some thought should be given to those that will be coming later to avoid painting yourself into a corner. It is also important to understand that the functional requirements will also influence many quality of service requirements. For example, the nature of the processes will affect the authorization scheme, the ability to process asynchronously will affect performance and scalability, etc.

Data Profile

The characteristics of the data to be managed will be a major driver of the architecture. Does the system deal with structured data, unstructured data, or a mixture? If the data is structured, does the functionality infer the need for a relational database or one of the NoSQL variants? Is the data sensitive (either wholly or partly), requiring extra security? How many records/documents will the system handle per day? How large are the units of data? Will the volume of data grow linearly or exponentially? Is there a need for archiving or near-line storage? Does the data lend itself to caching, either in whole or in part? Will there be a need to import data from a prior version of the system or from other systems to be replaced by this one?

This driver will have a major impact on the data architecture, infrastructure architecture and application architecture. Additionally, the characteristics of the data will affect a wide range of non-functional requirements: availability, backup requirements, certification, compliance, disaster recovery requirements, performance, response time, scalability, and security.

Audience

Who uses/will use a system significantly influences its architecture. Is the system intended for users internal to an organization, its customers, vendors, or some combination of these? Does the system interact directly with users, provide services to other applications, or both? Does the system have to support multiple platforms, either as a host or client (e.g. mobile)? Is accessibility an issue?

The answers to these questions will particularly impact quality of service concerns such as interoperability, portability, security, and usability. Likewise, this aspect is a significant driver of the infrastructure architecture.

Usage Characteristics

Question around how heavily the system will be used are as significant as the issue of who will be using it. Numbers of concurrent users and operation counts will have considerable impact. Another consideration will be the pattern of that usage: business hours versus around the clock. Will it be steady or spiky? If there are spikes, will they occur on a daily, weekly or monthly basis?

This driver will primarily affect quality of service requirements such as performance, response time, and scalability. If the usage pattern is twenty-four hours a day or nearly so, this will also affect maintenance and backup considerations too. The impact on infrastructure architecture is obvious, but it will also drive application and data architecture as well.

Business Priority

The nature of the system in relation to the enterprise, ranging from mission critical to minor, is a key architectural driver. Gartner’s application classes, as described in my previous post “Application Lifecycle Management Revisited”, may be useful in classifying the business priority of a system as well. Time to market can also be a component of this driver, particularly for systems of innovation. The business owner’s tolerance for risk will also fall under this category, with greater tolerance leading to less emphasis on the associated quality of service requirements. A system of innovation with a business owner having a high tolerance for risk will be a good candidate for quickly accumulating technical debt.

Business priority will drive application architecture, data architecture, and infrastructure architecture. Non-functional requirements associated with this driver include availability, backup requirements, disaster recovery requirements, maintainability, performance, response time, scalability, security, and supportability.

Regulatory and Legal Obligations

Regulatory and legal compliance can have as wide ranging an impact on application, data and infrastructure architecture as functionality and business priority. The salient feature of this driver, however, is that there will be little flexibility around it. If a regulation applies, then the solution complies (or else). This is not to say that there will be no gray areas with this driver, just that they will generally be questions of whether the system falls under the jurisdiction of a particular law or regulation rather than whether or not to comply if it does. HIPAA and Sarbanes-Oxley (SOX) are but two examples of the myriad potential legal obligations that could apply. Other examples of legal issues include licensing compliance (for both commercial and Open Source components and code), copyright, trademark, and patent issues.

Common quality of service requirements associated with this driver include accessibility, auditability, certification, compliance, and security. This is not to say, however, that others could not be impacted by legal and regulatory obligations.

Architectural Standards

If an enterprise has formal architectural standards, obviously they will affect application, infrastructure, and data architecture, including the full range of non-functional requirements. Absence of formal standards, however, should not be mistaken for absence of constraint. The existing environment (e.g. hardware and operating systems, development team background, support team background, etc.) will act as this type of driver in the absence of formal standards or where such formal standards are ignored.

Non-functional requirements associated with this driver will typically include accessibility, extensibility, maintainability, security, supportability, testability, and usability.

Audit Requirements

Arguably, audit requirements could have been lumped under functionality, but I listed it separately because of the variety of sources that these requirements can come from. Laws, regulations, the organization’s internal auditors, and even the business users may require tracking system activity. Audit information can also be valuable in determining usage volume and patterns, making operations another potential stakeholder for this driver.

Audit requirements will primarily drive application and data architecture. Obviously, auditability is the primary non-functional requirement affected, but performance, response time, scalability, security, and supportability can all be impacted as well.

Reporting Considerations

Like audit requirements, reporting considerations could be considered a sub-topic of functionality. I list it separately, however, because this driver can be both internal and external to the solution. Reporting that is integral to the solution can affect application, data, and infrastructure architecture (e.g. maintaining a dedicated reporting database to offload reports traffic is a common tactic). Enterprise reporting considerations can likewise impact the architecture of the solution, whether because of the need to support ETL jobs or to participate in master data management, as a supplier, consumer, or both.

This driver can impact quality of service requirements such as auditability (in situations where even reads must be logged), availability, backup requirements, performance, response time, scalability, and security.

Dependencies and Integrations

In “Managing Dependencies”, I outlined a number of ways in which the application architecture is affected by dependency management practices (see “Dependency Management – Anti-Patterns as Art from The Daily WTF” for some humorous, but true examples of how poor dependency management can affect architecture). Likewise, “Using Extension Methods for Message Transformation” and “Strict Versioning for Services – Applying the Open/Closed Principle” cover some of the effects of integrations (using services) on application architecture. Integrations and dependencies may also affect infrastructure (e.g. when connections come from systems external to your network or when using file drops) and data architecture (e.g. when using shared databases).

Non-functional requirements potentially impacted by dependencies and integrations include availability, disaster recovery requirements, interoperability, performance, response time, scalability, security, and supportability. Extensibility could also be potentially affected by this driver. A key consideration in the “Using Extension Methods for Message Transformation” and “Strict Versioning for Services – Applying the Open/Closed Principle” posts is structuring the system to avoid situations where two or more teams must conduct parallel development and releases to avoid breaking integrations.

Cost Constraints

I list this driver separate from business priority because it’s a fact of life that funds may be limited, no matter how critical the system may be. Salaries for development and operations staff; licenses for operating systems, database servers, and other dependencies; servers, storage appliances, network hardware and capacity all cost money (and this is far from a comprehensive list). These costs must be accounted for to properly understand their impact on the solution as a whole given that all aspects of the architecture are vulnerable to this driver.

Initial State

While this is the last driver in my list, it is by no means the least. New development may be subject to fewer constraints than changes to existing systems, but it isn’t without issues. Existing architecture can be both a limit to your freedom and also a base to build on.

In my opinion, these twelve drivers provide a framework for making architectural decisions for technology solutions, whether built, bought, or rented. It is important not to infer the importance of any one driver from its position in the list; that ranking will be a function of the priorities of the business owner(s) plus the requirements of the enterprise as a whole. Establishing the priorities will assist in balancing the conflict between favoring the simplest possible solution and providing sufficient flexibility to grow and change as time goes on. Additionally, these drivers provide a framework for ensuring that decisions are driven by need and not fads or dogma.