In 212 BC, Archimedes of Syracuse discovered the laws of buoyancy that explain why over 90% of an iceberg’s mass is beneath the waterline. Most of what is there is invisible at a distance. This is why ships give icebergs a really wide berth — so they don’t end up at the bottom of the Atlantic Ocean like the Titanic.
Software has a similar problem. Much of what it takes to build a world class software product lies well beneath the surface, invisible to the stakeholders that are most often paying the bills. There is no arguing that you have to build the right product to achieve any modicum of success. But once you are sure that you have the right product, you need to build that product right. That’s where engineering discipline steps in.
What we call “software architecture” is the discipline of advocating for the balance of the functional vs. non-functional requirements for a software product. In simple terms, software architecture’s role is to ensure that all the stuff below the surface of the product that you are developing has a voice so that the software has an amazing future. How you invest in software architecture is a proxy for how much you care about long-term software quality. The dilemma is that with most software projects, you are developing something that has never been built before. Pieces and parts may have been built previously, but the specific combination that you are building, and the user experience that you are putting on it, are unique. It is impossible to perfectly predict how the system will be used. If we spend our energy trying to make our predictions perfect, we forgo getting products and features to market and worse, we miss what is possible. We need to find ways to maximize predictability without restricting possibility.
In other words, what gets built needs to be flexible because we know that it will need to change frequently and in big ways. Because of this need for almost infinite flexibility, it is also impossible to eliminate all possible technical debt. The reason for this being that developers need the ability to experiment with different routines and procedures to optimize their solutions without perfect knowledge of what the future holds. When we optimize software, we have to strike an imperfect but purposeful balance between the non-functional requirements and the functional requirements. The software architect’s primary job in the context of a software product development is to be a voice for those non-functional requirements. Their charter needs to be continuously raising the bar on baseline quality through inspection, tools, automation, team culture and best practices. The dimensions of non-functional software quality are complicated and it is cost prohibitive to maximize every possible dimension. The imperative is to find the right balance for these longer-term investments.
The Software Architect needs to steward the tools, investments, policies and practices for the software products that are built so that they are:
- Future Ready. We are working to make sure that the product is not being coded into a corner. It has a future. This means that we are constantly looking for best practices around reusability, extensibility, testability and modularity. These are the things that will help the businesses we support maximize the flexibility that they get from their technology investments.
- Supportable. We are working to build best practices, processes and in some cases policies that make sure the product is appropriately fault tolerant, serviceable, testable, upgradable and sufficiently documented.
- Efficiently Built. We are aggressively experimenting with new technologies and ideas, but careful to steward our resources and looking for economies of scale in our tools, components and engineering practices. We are always striking a balance in how we invest in innovation and improving our execution. This includes things like managing source code control, automated software builds and continuous deployment.
- Risk Managed. We are aware of and appropriately managing the business risks, liabilities and compliance issues associated with our software products. This means that we are employing appropriate security testing and monitoring tools, we are addressing the source code licensing issues that can get us into trouble and we are competent in and complying with any regulatory statutes and standards that our business needs.
- Performant. We know how our products will scale and respond under expected load scenarios, both now and in the future, so we are able to support the growth of the business. We understand how responsive it is to our end users’ actions. We measure and manage concurrency and reliability and we make sure that it can be monitored for all of the critical business functions that it supports.
Building for any one of these “Iceberg Principles” almost always results in a trade-off for one or more of the others. The architect’s role is to educate the business on the consequences of the choices made around the non-functional requirements and being an advocate for informed choices. The key to managing technical debt for any software product is to be aware of these trade-offs and to put a system in place to test for whichever factors are important. Even better, working these testing tools into your continuous integration model is ideal.
If you don’t want your software products to end up like the Titanic, make sure you are finding the right balance between the functionality that the business demands and the aspects of non-functional quality that your product needs to ensure it has a viable future.