Thoughts About Communication and Code
Today is “bank holiday” in Ireland, giving me the time to reflect over the first four weeks of my internship at InishTech. It’s the first time for me to work with a real software company and one thing that I find particularly delightful is reasoning about code and design with great colleagues. Working solo on my past employers’ project (and only recently introducing a second developer to hand it over to) it has been one of my key motivations to find an internship opportunity where I could experience working on a developer team.
Talking about code and design all day made me realize how important it is to have a common understanding of the terminology we use to describe code and it’s design. Of course it is also important to have a common view for the problem domain you’re working on, but talking about the solution domain on its own poses interesting challenges enough.
Similar to the way a compiler assigns tokens to a series of input characters, we assign terms and descriptions to certain constructs. This can range from syntactic symbols (e.g. operator, variable declaration) to abstract concepts such as design patterns (singleton). Often, there are different terms we can use describe a syntactic symbol. As you can easily imagine, the number of opportunities we have grows with the “abstraction level” of the concept we try to describe.
For example when talking about “==” the “equality comparison operator” it is fairly easy to describe its behavior: “Returns true if the left hand and right hand expression are equal, otherwise false.”
But when we talk about a Singleton, things are not so concise any more: “Ensure a class has only one instance and provide a global point of access to it.” ( from the G04 Design Patterns book). We can imagine this design pattern has lots of different incarnations. Nonetheless, I regard the design patterns movement as a valuable contribution to our ability to effectively communicate concepts among programmers.
So far, I have noticed that communicating the lower abstraction level and the higher abstraction level is usually easy. The terminology we use here is pretty fixed and a variety of good definitions and “sources of truth” are available. For example recently I was writing a set of unit tests. Each test follows the pattern “arrange, act, assert”, which means that I configure my system under test (SUT), execute a command on it and then assert on the outcome. I wanted to split out the assert part to a different method because the outcome of the test depended on some complex external condition. My initial attempt had left me with a first method that contained the arrange and act portions of the test and a second that contained the assertions. The methods of the first kind were called something like TestXXX and the second were called ValidateTestXXX(object result). During code review, one of my colleagues pointed out that the prefix for the second kind of methods should be something different. After we popped the first ten books from my 1m tall book-stack on my desk (pictures to follow) he found a copy of xUnit Test Patterns: Refactoring Test Code and pointed me to the section where “custom verification methods” were described. Because what I had done was an exact implementation of this design pattern, we chose to prefix my verification methods with “Verify” rather than “Validate”.
What design pattern books do at the higher abstraction levels, language references can do at the lower abstraction levels. However, there is a grey area somewhere in between that I have found not so well covered. It might be that it’s just my missing formal CS education (which I am about to get soon 🙂 ) but I found it difficult to describe certain code constructs (like method parameters vs. arguments) precisely enough so that I can express small differences between two almost similar constructs. I will give concrete examples in a future post.
Another dimension of the communication problem is with the terminology used by APIs. Framework designers must pay close attention to use consistent terminology when naming public API’s and take care to document these terms precisely. But even when talking about the same API, the public facade might use a different terminology than the implementors do behind that facade. I’m getting the impression this is the case for the .NET Generics Implementation. I will do some further research to back that claim but stay tuned for my findings.
Coming from a solution-domain/code- centric view-point, it might also be interesting to see what challenges we have when communicating about our problem-domain. Eric Evans has a very good treatise of this subject in his DDD book. To facilitate communication among the developer team and with domain experts or users, he advocates using a “ubiquitous language” that draws its terms from the problem domain. Developing and refining this language is one of the core tenets of domain driven design. At InishTech we have a company-wiki that we use as an up-to-date reference of the terms in our ubiquitous language.