BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles 12 Software Architecture Pitfalls and How to Avoid Them

12 Software Architecture Pitfalls and How to Avoid Them

This item in japanese

Key Takeaways

  • People who are not building the architecture should not make decisions about it. Knowledge about how it is built is essential to making the important technical trade-offs that shape the architecture.
  • Quality attribute requirements (QARs) drive architectural designs. Ignoring or poorly defining these requirements is a recipe for failure
  • Don’t trust vendors to make your architectural decisions for you. They don’t know your context or your QARs and can’t decide your trade-offs for you.
  • Don’t copy your architecture from some other organization, no matter how successful they appear to be. They also don’t know your context and QARs.
  • The only way to evaluate an architecture is to build and test it. Delaying this to perfect the design is a sure path to failure.
     

Developing a successful software architecture is simple, but it’s not easy. Understanding QARs and then understanding and making the trade-offs that will maximally satisfy the QARs takes insight and experience, much of which has to be gathered through iterative experimentation on the architecture itself. The process itself is simple, but the trade-offs that need to be considered are often tough, and there are seldom easy answers.

Knowing where there are potential pitfalls often helps teams by telling them that going down a particular path will not take them where they want to go. In this article, we outline some of the fruitless paths that we’ve encountered, with the hope that they will better inform your decisions about trade-offs and consequences.

Don’t let one person make or influence all the decisions. Instead, have appropriate team members participate in making decisions.

Architecture is a balance between forces, the result of a series of sub-optimal trade-offs that are often never quite wholly satisfactory. Good performance, with good scalability, good security, good maintainability, and great usability? Good luck!

When one person, alone, makes all the decisions, the architecture will reflect their experiences, biases, and preferences. Sometimes these will be just right for the situation, but often they will be good in some areas and bad in others.

A healthy give-and-take between different people with different experiences leads to discussions that lay bare the competing trade-offs that a development team needs to make. Decisions made or influenced by a single individual, usually the most senior one in the team (otherwise known as the "HiPPO: Highest Paid Person’s Opinion" syndrome) rarely receive buy-in from every team member. They may not speak out against the decision out of concern for the safety of their jobs, but they will be quick to criticize, stop supporting the team, and even leave if something goes wrong as a result of that decision.

This doesn’t mean that the majority should rule, and architectures designed by committees tend to be bloated and unfocused. The ideal balance, in our experience, is when a few peers with diverse experiences and viewpoints challenge assertions to arrive at better-informed decisions.

Don’t let reuse goals dictate bad decisions. Instead, reuse only when it makes sense.

Reusing code, components, designs, or even configurations initially sounds like a good idea. Management likes to promote the concept believing that it will reduce costs and even result in faster delivery times, with better quality. A team may decide to reuse most of an existing application to deliver an MVP faster, or even to reuse an existing architecture that was created to deliver a fairly successful product.

Using services or classes/types is fairly easy and quite successful when the scope of reuse is a function because the scope of a function is narrow and its side effects are limited so that they can be used in very different contexts. Unfortunately, the expected benefits of reuse are rarely realized, at least at the architectural level, because architectures make broad and fundamental assumptions that are hard to adapt to different contexts.

Reusing an existing architecture is seldom successful unless the QARs for the new architecture match the ones that an existing architecture was designed to meet. Past performance is no guarantee of future success! Reusing part of an existing application to implement an MVP rapidly may constrain its associated MVA by including legacy technologies in its design. Extending existing components in order to reuse them may complicate their design and make their maintenance more difficult and therefore more expensive.

When evaluating reuse opportunities, ask yourself if doing so makes your architecture more complex. If it does, you may be better off writing it yourself to exactly meet your needs.

Don’t get rid of people with experience working through architectural challenges. Instead, retain them and retrain them if necessary.

Management is obsessed with reducing cost, sometimes motivated by bonuses that incentivize managers to reduce cost by a specific percentage. Empowered by a belief that software development skills are a commodity, they may be seduced by promises that low-cost vendors can provide the same skills as team members with years or decades of experience.

Sometimes this is true. As a former colleague once observed there’s a big difference between having ten years of experience and having one year of experience repeated ten times. Put another way, the real skill in software development is not coding, or knowledge of syntax, or familiarity with a particular set of frameworks–software development is problem-solving.

Architectural work is problem-solving, with the additional skill of being able to make trade-offs informed by experience in solving particular kinds of problems. Developers who have not had experience solving architectural problems will learn, but they will make a lot of mistakes before they do. It can be cheaper to hire or retain people who have already gone through that learning cycle than to assume that all you need are smart people, regardless of their experience.

Don’t let business decisions dictate your architecture. Instead, make decisions to meet well-defined QARs.

Business decisions are often short-term in nature: quarterly and annual planning cycles tend to dominate business decisions. Executives who focus on the future at the expense of current period results often don’t stay in their positions long enough to see their long-term dreams realized.

Software architecture has to be different. While immediate results are important, the investments organizations make in building systems often require years to break even, and many systems have life spans measured in decades. While the business wants immediate results, it also cannot afford to build a new system to solve a particular problem every few years.

Nevertheless, business people sometimes try to inject themselves into architectural decisions, usually emboldened by some recent article they read about how "blockchain is the latest thing" or, more recently, how "generative AI is going to change everything and companies that don’t jump into it now are going to be left behind", to provide two examples. Technology does level the playing field in ways that upset the status quo, but never quite in the ways that pundits predict.

While new technologies offer interesting capabilities, they always come with trade-offs and unintended side effects. The new technologies don’t fundamentally or magically make meeting QARs unimportant or trivial; in many cases the ability of new technologies to meet QARs is completely unknown. This is where experience dealing with architectural issues is important: knowing where and how to ask the right questions, and then knowing how to devise experiments to obtain answers to those questions.

Don’t compromise quality to deliver faster. Instead, manage your Technical Debt at a level that keeps your architecture viable.

There is always a tension between the Minimum Viable Product (MVP) and its associated Minimum Viable Architecture (MVA). The purpose of the MVP is to test whether a solution improves the outcomes that customers/users experience. The purpose of the MVA is to ensure that the MVP can be economically and technically supported over time. If the MVP isn’t valuable, then the work on the MVA is wasted, but if the MVA isn’t viable, the MVP doesn’t matter either.

Organizations that focus too much on the MVP can find themselves with unhappy customers who like the concept behind a product but who become disillusioned with how poorly the product performs. This leaves the door open to competitors who can simply copy the MVP but implement it more effectively over time. There is a reason why the so-called first-mover advantage (the benefit of being first to market with a solution) is over-rated: customers punish companies who deliver shoddy products.

With the rise of agile software development approaches and practices like refactoring, some organizations have been seduced into thinking that speed is all that matters because they can always fix/refactor the problems later. The reality is that there are limits to the effectiveness of remedial work. Remedial work, what "fix it later" and refactoring really mean, is expensive because of the time teams have to spend figuring out what code does before they can rewrite it in a different and more effective way. Calling it refactoring or using an agile software development approach doesn’t fundamentally reduce the complexity of the work.

Don’t delay delivery (and feedback) to perfect your architecture. Instead, design your architecture using the best information you have and use feedback to improve it.

The preceding section may lead some readers to believe that software development teams should take care and time to never ship a flawed architecture. That’s not a good strategy either. No architecture is perfect; it is shaped by a set of imperfect trade-offs, some of which are wrong but can’t be determined to be so until the system is operational. And sometimes not even then, until something really unusual occurs. Believing that the architecture can be made perfect, defined once and for always, is a dangerous mindset that prevents teams from developing a resilient and adaptable architecture that can evolve to meet unforeseen needs.

The "Big Architecture Upfront" syndrome is often fatal to a system. Even when not completely fatal, it unnecessarily delays the release of the system that initiates real learning. It’s not possible to nail down all the architectural requirements before designing this system. This is not an argument against creating an initial architecture; everything has a starting point. But basing the initial architecture on the best information you have at the time and then using feedback to improve is better than continually delaying the initial release hoping to get new information.

It’s also worth noting that no amount of review meetings can substitute for actually building at least a subset of the architecture and testing under a variety of conditions to evaluate its fitness for purpose. Review meetings, even when conducted by experienced architects, can only uncover problems the participants have experienced before, not emergent problems that can easily surface when new techniques or technologies are employed.

Don’t let functional requirements drive the architecture. Instead, ensure that it is driven by realistic QARs.

Everyone agrees that requirements are important, and as a result, most development teams spend the majority of their time developing solutions to functional requirements. Working on functional requirements is relatively straightforward, as business stakeholders are usually quite articulate about what they want, while they struggle to articulate quality attribute requirements.

Unfortunately, good architectural designs are driven by well-defined quality attribute requirements, and designing a software architecture using only functional requirements will create a software product that may not scale well, may not perform well under load, or is not resilient over time. If a development team only focuses on functional requirements, the architecture of its solution will most likely be inadequate to meet the real needs of its users.

Don’t copy someone else’s successful architecture. Instead, design your architecture using your own QARs.

Popular articles and conference talks describe how some famous companies, or some vendor, used a particular approach to satisfy a particular QAR. These talks and articles share valuable insights that are important sources of learning, but they have their limits. The same is true for so-called "Best Practices" or "Architectural Patterns". Knowing that someone else was successful using a particular approach is useful, but only to a point.

Every architecture is a balance between different forces, a set of sub-optimal trade-offs that make sense in their context but often don’t translate well to other contexts. Understanding the forces involved and possible trade-offs for your own solution is essential because they may lead you to a different conclusion than the famous company at the conference ... and you can both be right.

To make sense of someone else’s choices, you have to understand their context and QARs. Just knowing the final choice they made does not tell you very much. Basing your decisions on just their choices without understanding their context can lead you down the path of failure rather than replicating their success.

Don’t outsource your decisions to vendors and consultants. Instead, make sure that you retain control over your architecture.

A variation on copying someone else’s architecture is to outsource (or abdicate) the architectural decisions to vendors or consultants who claim to have experience with similar problems. Their solutions may work well in other contexts, but you still have to evaluate their offerings or ideas for yourself. Their offerings may prove perfect for your situation, but if they don’t it’s your problem to fix, not theirs. Understanding this from the beginning helps you to ask better questions that inform better decisions.

Consultants can bring much-needed expertise and different perspectives to your organization, but they are not omniscient and they, too, have blind spots. They can advise you in your decisions but they can’t make them for you.

Using open-source frameworks without understanding their QARs, security vulnerabilities, maintenance issues, and licensing issues is, in effect, another way to outsource decisions. Open-source components are an essential part of any modern application, but they may or may not support your QARs. Before you adopt them, you need to understand what decisions their creators have made and whether those decisions are right for you.

Don’t over-generalize the architecture. Instead, design for your QARs and only your QARs.

Software architectures are not universal; they reflect trade-offs that are context-dependent and often application-dependent. A software architecture is good so long as it meets its QARs.

Sometimes this feels like it’s not enough, that an even more general solution exists that will solve an even larger set of QARs to provide a common architecture across different kinds of applications in an organization. But there are no bonus points for solving a more general problem, and no proof that such a general architecture is needed or will ever be used. The second revision of many industry standards falls into this trap, trying to meet any conceivable need that might come up, and in the process becomes bloated beyond usefulness.

Don’t build the architecture in one go. Instead, build and test it in small increments to reduce risk and waste.

Software architecture is software; the only way to know if it meets its goals is to have goals in the first place, and then to build at least part of it and see if you are making progress toward those goals.

The goal for a software architecture is for it to satisfy its QARs. Or at least to make a reasonable set of choices so that most of them are satisfactorily satisfied, because it’s not always possible to meet all of them perfectly. The skill in architecting is balancing the trade-offs.

When the "big-up-front architecture" approach fails, it does so because some of the information a team needs to make trade-offs can only be obtained by building and testing some portion of the architecture. No matter how smart or technically experienced development team members are, the architecture will probably have to handle things that they have never seen before. As a result, they need to experiment.

This does not mean that architectural peer reviews are unimportant, especially when peers (outside the development team) may have some experience that can save the development team a lot of work. Or at least point them in a direction that may be fruitful for exploration. Skipping architectural peer reviews "because we don’t have time for this if we want to meet our delivery deadlines" is usually short-sighted and can lead to more rework later on.

[20 Dec 2023 editor’s note: The following section was added after original publication.]

Don’t rely only on internal software architecture reviews; release the product as early as possible and get real-world feedback

In the initial release of this article we made an error; we had only eleven pitfalls, even though the title promised twelve. Countless reviews by authors and editors missed the error, but a careful reader caught it. Like the classic "off by one" array indexing problem when coding, the error was very hard to spot. More detailed reviews might have caught it, but the people who are closest to such a problem are focused on other things. It takes someone with a fresh perspective to find what the original authors/developers cannot.

In the context of software architecture, which is far more complex than an article, the only way to get this perspective is to get a working version of the product into the real world. Once the system is running and meets its Definition of Done, the only way to improve the architecture is to subject it to real-world scrutiny, free from the biases held by the people who developed the system and their community of collaborators.

Conclusion

It is hard to say explicitly what will lead to a successful software architecture, but it’s easy to say what will not, as we have outlined above. Looking to other people to define your software architecture, or copying someone else’s architecture are two big ways to fail to create a successful software architecture. Failing to consider your QARs is another one, as is expecting magical vendor technology, or a process, to do it for you.

The dysfunctions that can damage a software architecture are almost limitless in number. The list we’ve provided here, however, gives you some idea of where things can go wrong and, we hope, some suggestions for what to do about them. You will, undoubtedly, encounter others. As we recall someone saying a long time ago, "good judgment comes from experience, most of which comes from bad judgment."

About the Authors

Rate this Article

Adoption
Style

BT