Principles of Software Engineering
I recently watched a presentation by Peter Campbell from 2016’s O’Reilly Software Architecture Conference in London. The presentation was titled “Architecture as Belief.” The talk’s emphasis was on identifying and discussing the fundamental principles that underly decision-making as software architects. Peter did a very good job identifying that software architecture is not primarily about making technical decisions, but instead about expressing the principles that we believe lead to good software design.
Peter outlined six of his own principles as part of the talk and it got me thinking about what I value as a software engineer. As I’ve been looking to be more deliberate in my own thinking and the way I support the teams I work with in regards to software architecture, I decided to take the advice that Peter offered, and present some principles that I think are valuable in pursuing good software design.
Clarity as a principle of software design relates to intentionality and ease of discovery. Something that is clear should require less explanation and should exhibit a degree of self-descriptiveness. This principle can also be thought to stand in some degree of contrast to brevity. I do not believe that brevity is inherently valuable, unless it also possesses the property of clarity.
This principle is most easily understood in relation to code, but also has applicability to the overall design and construction of a software system. In terms of implementation the principle of clarity often translates into taking more discreet steps to achieve an end, rather than pursuing clever solutions. Performance can be a trade-off in some cases, but maintainability and changeability tend to be improved with higher degrees of clarity.
At higher levels of abstraction the application of the principle of clarity can become a bit less obvious. To me the notion of clarity at a system level would mean that the paths of data through a system is obvious. It would mean that the relationship between modules and components are intuitive given a degree of understanding of the problem domain. As noted at the outset, a good way to think about this principle of clarity would be seeking the characteristic of being intention revealing. In that way, the application at all levels of the principe of clarity becomes more clear itself.
Code that is straightforward to test also tends to have the property of being straightforward to use. This same property also applies to systems as a whole. Testability also has a connection to the other principles, particularly that of clarity. Code and systems that reveal more about their intention and proper use tend towards a higher degree of testability. Additionally, code that is testable tends to be code that becomes easier to trust over time.
Some of the specific properties that allow code or systems to exhibit the principle of testability include smaller public interfaces. The larger a public interface to something becomes, the more complicated it can be to reason about and thus to test. So, pursuing public interfaces that express a limited number of actions will tend to yield more testable code. The principle of clarity also comes into play in that interfaces that are clear about their intention also tend to be more easily testable.
This principle could also be called changeability, but I prefer the term evolvability because of the implication that evolution leads to improvement in regards to some notion of suitability for a purpose. Very few software systems are static, and as such very few should not exhibit some degree of evolvability. Additionally, all processes likely have areas they can be improved. For these reasons I think evolvability represents a more transcendent principle in relation to software than the previously presented principles of clarity and testability.
An evolvable system, or area of code, is one that does not resist being changed via high degrees of coupling or possessing too many responsibilities. There are other factors that tend to cause change to be difficult, but coupling and responsibility glut are the most common in my experience. Evolvable software thus should exhibit qualities of being loosely coupled and maintaining focussed and high cohesion with regards to its responsibilities. These qualities apply equally at every layer of abstraction within a system, but with differences of scope and scale.
The principle of openness is one that I feel applies more to process and culture than to the way code is written and systems are designed. Part of that though is how I want to define the boundaries of openness in regards to communication, collaboration, and sharing within a team context.
Having worked on a number of teams of various sizes and with varying processes, one of the things that I’ve observed that tends to lead towards better productivity and team cohesion is a free flow of ideas, and information. Teams and cultures that value openness tend to be cultures with more well developed approaches to teaching and learning. Practices like pair programming rely on a fundamental commitment to some level of openness.
Openness also means that ideas from all sources are treated honestly, and respectfully. Openness does not mean that all ideas are pursued, but they’ve are valued as a prompt for conversation. Openness also should lead teams towards holding onto ideas loosely and avoiding calcification. In this way, openness is tied to evolvability as a contributing principle with respect to culture and teams.
For each of the principles above my goal has been to summarize the meaning of each principle, as well as described some of their implications. I don’t consider this list exhaustive, and I’m not confident that all of these will be principles I claim to value in the future or in all possible contexts. But, right now, given my experience and current opinions, I think these principles are worthwhile. I would enjoy if these ideas could lead to further discussion of principles that undergird how and why we write software the way that we do.