In the blogpost about structural idealization I explained how structural engineers model reality to answer the question whether their designed structure can withstand the forces applied to it. But how does an engineer know which forces will be applied to their structure? The weight of the structure can be approximated at best, and natural occurrences like wind or snow or a car crashing into the building are much harder to anticipate and quantify. And how does an engineer consider a car crashing into the structure while still building reasonably economical?
In this blogpost I’ll give you an introduction into how I as Structural Engineering student was taught to tackle this challenge. By drawing parallels between Structural Engineering and Software Engineering I’ll try to answer the question: What can Software Engineering learn from Structural Engineering regarding reliability?
Structural Engineers use Limit State Design to express the structural reliability of a building with regards to so called Limit States.
Limit States #
Limit States are boundaries that a building may not cross. Beyond the Limit States the structure has lost stability, has endangered human life or is simply no longer usable.
Limit State Design compares the Effect of ‘actions’ (= loads) to the Resistance of materials (which will be another post) and the structure. The goal is to verify that the loads never exceed the resistance, keeping the structure below the limit at all times and in all relevant situations.
Ultimate Limit States #
Ultimate Limit States are states associated with collapse or other similar forms of structural failure that endanger the safety of people, and/or the safety of the structure. The actual Ultimate Limit States that need to be taken into consideration can differ from structure to structure and from surroundings to surroundings.
Examples of Ultimate Limit States include a footing that slides, a column that buckles or a roof that collapses.
Ultimate Limit States focus on safety, on the structure unintentionally becoming a threat. Examples for States where a Software System unintentionally and without malicious intent becomes a threat are a higher traffic than anticipated, bringing the server to its knees, cascading failures resulting from one failing service (availability), a corrupted database (integrity) or accidentally granting users more rights than they should have (confidentiality).
Security, which is usually not a priority for the structural design of buildings, is a priority for Software Systems. Security introduces a distinct category of Ultimate Limit States, those caused by deliberate action rather than accident such as DOS attacks (availability), someone reading data in transit (confidentiality), someone maliciously changing data records in a database (integrity) and other intentional attacks.
Whether the cause is accidental or deliberate, the effect is the same: a destabilized system. As is the goal of both Structural Engineering and Software Engineering faced with Ultimate Limit States: preventing the system from causing harm by maintaining system stability.
Serviceability Limit States #
Serviceability Limit States are all states that touch the usability of a structure under normal use.
Examples for problems resulting from exceeding a Serviceability Limit state are machines in the building that can no longer operate because the floor is too deformed, or because of excessive vibrations. The Serviceability Limit State is also exceeded when people perceive the building as unsafe. One cause can be deformations, that are not safety-critical (they are considered safe in the Ultimate Limit State verifications), but large enough to make people feel uneasy.
Large vibrations that have no effect on the stability of the system can cause discomfort in people too and need to be limited to comply with the Serviceability Limit State verifications as well.
Only satisfying the Ultimate Limit State is not sufficient. Both Limit States must be satisfied for the intended use of the structure.
Even in Software Engineering your system needs to be secure, stable AND useable as intended.
It is not sufficient that the system is secured and availability and uptimes are great. ‘Nines don’t matter when users aren’t happy’, meaning that it doesn’t matter how stable a system is, if the users cannot use it as they intended. There are many different reasons why users aren’t happy, the latency can be too high, the navigation is unintuitive, the application has accessibility issues making it unusable for a group of people, the error messages are meaningless, or a button is placed somewhere where the user cannot find it. It is enough for users to simply dislike the experience for the whole system to be seen as a failure.
Determining Loads #
In order to quantify the loads the Structural Engineer needs to know what situations to consider and for how long the building needs to withstand the actions.
Design Working Life #
Structural Engineers pick a reasonable Design Working Life for the structures they design. For a festival stage, that is supposed to stand for a month the probability that it is hit by a once-in-100-years storm is much lower than it is for a permanent structure like a bridge. So generally, the intensity of environmental loads (like wind or snow) can be adjusted based on the structure’s lifetime.
In Software Engineering it helps to determine the Design Working Life of a system and adjust the robustness and maintainability of the system accordingly. A short-lived application can afford to optimise for speed of delivery. A long-lived one needs to optimise for the people who will maintain it, who may not be the same people who built it.
So determining the Design Working Life of an application and communicating it clearly is important to finding that balance between reliability and economics.
Design situations #
A Design Situation describes a situation the structure can be in, that needs to be considered during design.
Design Situations are grouped in categories, most interesting in the context of Software Engineering is the distinguishing between the Persistent and Accidental Design Situations. So that is what I’ll concentrate on.
The Persistent Design Situation refers to the normal use of the structure from when it is completed to the end of its Design Working Life. Every load that is likely to occur under normal use needs to be considered. So for a house with a Design Working Life of 50 years the once-in-50-years storm is found from history data and used as a load that the structure must endure.
To determine the design loads for the Ultimate Limit States and the Persistent Design Situation the expected loads are multiplied by a safety factor greater than one, creating a deliberate margin above what is actually anticipated.
For the Serviceability Limit States and the Persistent Design Situation (usability under normal use) however the actual loads are not being increased with a partial safety factor.
The Accidental Design Situation deals with exceptional situations, like fires, explosions or human errors. Because it is unlikely that the 50 year storm and the Accidental Situation occur at the same time the actual wind loads (and other environmental loads) are decreased for the verification. Actual loads are generally not being increased with a partial safety factor in the accidental design situation either. It is important that the structure does not collapse and that is what is designed for in this Design Situation. It is accepted that the building can not be used as intended when an accidental situation occurs.
This approach adds a safety net where it counts. Still designing for useability and accidents, but intentionally accepting a higher reliability risk to build economically.
In Software Engineering we can apply the same thinking. We can do so by adding a margin to what we expect our system to be necessary to handle. We can for example make sure it is easily horizontally scalable, we can build in redundancy, an error queue and retries. For accidental situations (a DOS attack or a security breach), we accept that the system cannot function as intended. But we deliberately decide in advance, before an incident occurs, what that means. What is the minimum the system must still guarantee? Protecting user data from being accessed or corrupted is likely non-negotiable, everything else might be. We could decide to drop non-critical requests, use rate limiting or apply exponential back-offs, accepting that a legitimate user might be slowed down or temporarily blocked as a side effect.
Consequence classes #
Depending on the consequences a failure would have to a structure or its parts it is assigned a Consequence Class. This Consequence Class can then increase or decrease all loads in the persistent Design Situation (under normal use).
It also dictates how much supervision is needed during design (from self check to checking by another organization, the normal is checking by another person) and construction.
Which Consequence Class applies depends on multiple factors. One is of course the possible loss of human lives, but economical, social and environmental consequences weigh in as well. So the loss of faith of the public in an organization or the government can justify a high consequence class.
It seems intuitive to concentrate on the consequences of a failure and build in a buffer into systems whose failure would have extraordinary consequences. And on the other hand deliberately and consciously accepting lower robustness for lower-consequence systems. This way we know why we are chasing metrics (like Nines) and when we should put our efforts elsewhere. When thinking about consequences we should think about all negative consequences. It is obvious that safety-critical systems need to be designed with care, but economic and social consequences should be considered as well, even in Software Systems.
Risk Strategy #
Not every situation needs to be checked for every single project. A small house in a non-earthquake zone might not need to be designed for the Seismic Design Situation.
Picking the right Design Situations is hard, especially for accidental situations. The engineer needs to balance the expected consequences of failures with the cost of the construction work.
Another strategy is to minimise the risk of the accident occurring in the first place, rather than designing to withstand it. Instead of increasing the resistance of the bridge to withstand a ship crashing into a pillar, the pillar can maybe be moved to the land? Or maybe the pillar can at least be protected in another way?
Changing the strategy from designing for all possible accidents to risk avoidance applies to Software Engineering as well. At a smaller scale it could be something like implementing exponential back-offs, at a larger scale it might be using the principle of least privilege. Zooming out and trying to see a problem from another angle can be powerful in all (engineering) situations.
This is reflected in the risk mitigation strategies according to OWASP. For each risk you make an informed and documented decision about whether to accept, eliminate, mitigate or transfer the risk.
Design Simplicity #
Risks can also be decreased and the durability of the structure increased if you make it easier for the people building the structure to work their best. That is something that was stressed a lot by my professors. If your design is complicated, people will have a hard time applying it. It is absolutely worth it to pick materials (like screws and reinforcement bars) in your design that are stronger than need be in order to keep the total number of different materials to pick from at a minimum. When I studied, it was even mandatory to work on site for 3 months, in order to get a feeling for how it felt to work in the environments I as a Structural Engineer would be creating.
This is a lesson I have learned first hand in Software Engineering. When systems are designed without the people who will implement them, the design is unlikely to survive contact with reality, because the code is the architecture. This leads to frustration and systems that can only be navigated by the few people who build it. The best way to find out if a design is too complex is to collaborate with the people who have to implement it. It is generally important to not overcomplicate things and sometimes it is even worth a slightly clumsier design if it makes it easier to understand. So choosing a monolithic architecture instead of microservices, or communicating via HTTP instead of using an event-driven architecture can be a humble move if that is what the team understands and believes in.
Conclusion #
The process of Limit State Design is very structured. It forces the engineer to think about how to build reliable structures and what reliability means for the structure, before they start iterating and calculating. Although software is soft and therefore changeable I think a ground level of structure can help to build systems that are as robust, reliable, useful and maintainable as they need to be. By clarifying:
- What might threaten the stability and security of our system (Ultimate Limit States)
- What might threaten the usability of our system (Serviceability Limit States)
- How long does this system need to be alive (Design Working Life)
- Which extreme situations might occur, and are there other ways to prevent those (Design Situations & Reliability Management)
- What are the consequences of the system failing (economical, societal, human lives) (Consequence Classes)
and balancing these points with economics we can create a foundation that helps us to decide what failure states to focus on when we are in the middle of an iteration.