TLDR; Abstractions are not new, have never been easy, and have always been important when architecting our Systems.
Advances in Computer Architecture - by Glenford J. Myers is probably an old book in computer terms - the second edition was published in 1982 (first edition in 1978) and I was expecting it to be a curio. But I found it interesting because it was focused on Computer Architecture in terms of “viewing a system as a set of levels of abstraction.” And I use the ‘abstraction’ word a little too often, making this book relevant to my studies.
Given a set of abstractions, then look at the function boundary that the architecture deals with and the interface that it provides to the user. Myers also identifies that some Architecture levels are hard to show on a level diagram because they require vertical rather than horizontal cuts.
Abstraction layers overlap because they deal with different use cases and different users. If we only horizontally layered abstractions then we could make the case for overly tight coupling and too many layers. If we accept vertical, horizontal, diagonal and ‘other’ cuts then we are more likely to concentrate on creating abstraction layers that make it easier for the user to solve problems rather than for the implementor of the abstraction layer.
“Computer Architecture is the abstraction or definition of a physical system as seen by a machine-language programmer or a compiler writer” pg 6
On page 17 Myers discusses The Semantic Gap. In the book Myers is concerned about large Semantic Gaps between programming concepts and operating system implementation because either the application writer has to write code to bridge that gap which can make the applications more error prone or longer to write.
We often turn to libraries to help us plug these semantic gaps when writing applications.
The remainder of the book is a discussion of different architectures looking at how they support the writing of applications and the overlap between the abstractions required to model the system and the implementation in different machines and processors.
I found the final chapter relevant to my work when automating applications and constructing abstraction layers to make the automated execution easier to write and maintain. The final chapter lists some goals and tools for consideration by the architect:
- Strive for Conceptual Integrity
- Strive for Orthogonality
- Know your audience
- Optimize for usage
- Design for Extensibility
- Strive for implementation independence
- Avoid a Technology Dependent Design
- Do a formal description of the architecture
- Be a compiler periodically
- Prove a mapping from language to machine
- Avoid Committee Decision Making
I’m going to interpret the above based on my understanding of the book and my prejudices regarding automated execution.
Strive for Conceptual Integrity
Aim for some consistency throughout the abstraction layer as this helps people use them. i.e. don’t have some methods on Page Objects return other Page Objects due to a navigation process and then other methods which also navigate return
void or a domain object. Either do return Page Objects or use Navigation Objects - this principle doesn’t mean ‘get it right’ it means ‘decide’.
We can do this by refactoring our code - based on experiences of using it to automate the application. Coding to interfaces can also help.
Strive for Orthogonality
Have a separation between ‘domain objects’, ‘page objects’, ‘data objects’ etc. Don’t munge them together and expect people to understand your architecture. Do your best to separate concepts. Organize them into effective packages. This should make it obvious to find the ‘approved’ way of doing something
Know your audience
Build the abstraction layers to meet the needs of the people reading it, use their language and concepts.
Don’t make it easy to implement, make it easy to understand.
Optimize for usage
I aim for flexible use. I tend to write the code I want to see, i.e. how I want to use it, rather than how I think it would be easier to write.
This also helps orthogonality because we will put functions in the interfaces that we use rather than where they are easiest to add.
Design for Extensibility
I try to avoid Inheritence and any smell of Framework. Because I want my abstraction implementation to be viewed as a Library that the user can easily build on.
This boosts flexibility and allows abstraction implementations to be used for Performance Testing, STtress Testing, adhoc Exploration, and defensive checking in Continuous Integration.
Strive for implementation independence
In my book Automating and Testing a REST API I show an example of using RestAssured where the actual use or RestAssured is isolated in a ‘messaging’ abstraction that would be easy to re-implement in another HTTP library. My @Test abstractions were independent of the implementation.
Avoid a Technology Dependent Design
Ideally I try to write my code to be as cross platform as possible because I will probably want to run it on Windows or Mac for development and Linux for Continuous Integration.
I want my abstraction layers to synchronise effectively on application state, rather than rely on timing from a single test environment.
I want my assertions and synchronisation to work cross browser and not be limited to only running on Chrome or Firefox.
Do a formal description of the architecture
Create a set of @Test methods which demonstrate how to use the architecture - make sure they run and then they can form a ’living documentation’ or ‘formal description’ of the architecture.
Be a compiler periodically
For most of the @Test code I write - efficiency isn’t the most important consideration but every so often, refactor your code for efficiencies and look for alternative collection classes that might match your problem domain better.
Prove a mapping from language to machine
I struggled to map this. So I didn’t.
Avoid Committee Decision Making
Abstraction layers are opinionated. If we make them easy to extend then other people can add their opinions in the relevant parts of their architecture. If you concentrate on usage then you may not have to worry so much about this. The danger is if you “can’t agree, so we’ll make it work both ways” is more likely to confuse the user and lead to maintenance overhead in the long term.
I like to read older computer books because we still wrestle with the same concerns and approaches now (its only 35 years old, its not like it was written 000’s of years ago).
I don’t think this book is for everyone I rattled through it because the indepth discussions of the different machines didn’t seem as relevant on this read through as the general concepts.
Myers does have other books that are still very relevant.
- Software Reliability
- Composite Structured Design
- Reliable Software through Composite Design
And of course “The Art of Software Testing” (but I think the three books above are far better than the Software Testing book).