Seeking Concinnity in Coding Conventions
Does your team use a formal style guide or coding conventions? Do you know how those conventions were decided? How do you make guidance changes as your team evolves?
I created a mental model which has proven compelling for improving developer culture over the last 15 years at Amazon and Microsoft. These four principles have consistently helped me construct, socialize, and implement developer guidelines in every team regardless of the language or domain. I also use them when doing design and code reviews, especially for novel situations where conventions aren't clearly defined. Many teams have added variations of them to their own guidelines.
Use them as a leader to explicitly guide discussions, or implicitly as an individual to be more persuasive.
Epiphany of Concinnity
I love code. Good code is similar to a good story or song; I don’t think about how it was written and instead get to immerse myself in the process and concentrate on the purpose and outcome. The roles and actions flow together, and the language is just a vehicle for the expression of intent.
Back around 2005, I was looking for a meta-layer to approach coding guidelines that would be more language, framework, and team agnostic. I didn't find what I was looking for in the programming literature.
However, I was also reading The Diamond Age by Neal Stephenson (which I recommend), when I saw the word 'concinnity' for the first and only time in a book. It rolled smoothly off the tongue and felt familiar, like I should already know it. When I looked it up, it snapped into place, fitting right into what I was trying to do.
concinnity: harmony or elegance of design especially of literary style in adaptation of parts to a whole or to each other – Merriam-Webster
And for me, concinnity captured in one word the qualities of all beautiful code: linguistic, aesthetic, intentional, connected, and harmonious.
Principles of Concinnity
I constructed four principles centered around a feeling captured in one word. I was intentional that I would use them in dialog with myself and others while nurturing code culture. They have been especially useful when selecting and explaining specific guidelines as I contributed to developer documentation. I apply them in priority order.
P1. Predictably Correct
Verify the behavior of the code reflects the intent. Code with concinnity sets assumptions and then keeps them. I am more trusted when my code speaks for itself. I don’t have to control my code. I prefer finding practices that avoid pitfalls.
P2. Cognition Aware
Empathize with future developers from diverse cognitive backgrounds. Code with concinnity meets the needs of the present without compromising the needs of future generations. I am more effective and influential when I am reaching developers at different levels of familiarity and experience. I don’t have to prove that I am clever. I prefer kindness to many readers over convenience for few writers.
P3. Internally Conformant
Share the same expectations across the groups of people that share the code. Code with concinnity is consistent with the code closest to it. I am more efficient when I align my decisions and outputs with those of my peers. I don’t have to constantly debate alternatives. I prefer norms over originality.
P4. Externally Compatible
Reuse existing standards, technologies, and practices, especially from dependencies. Code with concinnity is compatible with connected communities. I am more agile across projects and organizations when I can also transfer assumptions. I don’t have to customize everything for new situations. I prefer to make my habits portable.
Application of Concinnity
The purpose of having coding guidelines centers around improving quality of the code, efficiency development, and team cohesiveness. It can also codify the developer culture.
As developers, we love debating naming conventions, especially when establishing our guidelines. As a case study, I will use my principles to evaluate the most common naming convention for constants and enum elements in Java. I will use Google’s style guides as a common point of reference, since they are public, popular, and thoughtful.
When we use Google’s style for Enum Classes and Constant Names, we have get an example where the class name is PascalCase and the elements are CONSTANT_CASE (instead of camelCase like normal class fields).
enum Priority { LOW, MEDIUM, HIGH}
In normal writing, people use all caps to convey emphasis or yelling (another name for this pattern is SCREAMING_SNAKE_CASE). Testing shows that it’s also less legible than alternatives. Personally, I would prefer to reserve all caps to situations that require alerting future developers to a risk. This is applying the Cognition principle, since it shows empathy for the people that have to read it later. So maybe we are seeing a different principle in action.
Correctness is higher priority, but when applied here, I find this naming for constants to be misleading, probably causing more errors than it prevents. By using caps, readers will predict that constant means immutable. But deep immutability is actually pretty hard to get right in Java, so if the constant is more complicated than a string or primitive, then this assumption is easily broken.enum
enum Sequence {
ODD(new int[] {1, 3, 5, 7}),
EVEN(new int[] {2, 4, 6, 8});
public int[] vals;
Sequence(int[] vals){
this.vals = vals;
}
}
// ...
Sequence.ODD.vals[0] = 2; // immutability of ODD is busted.
Ouch! The Google calls out examples of how easy it is to get wrong in Constant Names. Using constant case is actually creating pitfalls, and I have seen this in the wild with common platforms.
The top principles are effectively violated, but we know smart people came up with this convention. What were they thinking?
Probably this is because the JVM was written in C. We know that Java was intentionally designed to be part of the C-family, making the Java code easier for C-devs to adopt. Java aligning with C leverages the principle of Externally Compatible.
But in C, all caps really means MACRO_CASE, indicating that the code will be expanded by the C-preprocessor before it gets to the compiler. Macros in C are a really important and powerful part of the developer arsenal. But they can also introduce infinite surprises, making them quite dangerous when used without care. Macros can affect precedence, scope, and double execution, which all introduce non-obvious problems.
For examples, you can check HRESULT_FROM_WIN32(), which was the source of many hard to notice double execution bugs in Windows. Or look at logging and debug macros for more genius level horror in the power of macros. So, C-macros are a really good candidate for being all caps!
MACRO_CASE is also used for declaring constants in C. So constants are macros, but macros are not constants. but while they can imply constancy, a developer can’t assume it until checking out the specifics. The proper notion of const is still part of the compiler not the C-preprocessor. But I remember that for a lot of C-code they were interchangeable. And C enums were a blurry spot, they looked like macro constants, but were resolved by the compiler not the pre-processor. All of which in hindsight seems pretty bad for Correctness, but makes it pretty easy to see how early Java developers, who were also C developers, organically transferred the MACRO_CASE in C to CONSTANT_CASE in Java.
My guess is that an original group of active developers rationalized that while macros didn’t exist in Java, that all caps should still be meaningful, and that constants were the simplest Compatible concept. The creators of Java packages were also the C-devs for JVM so keeping the same case conventions across languages also follows Conformance.
Not all languages followed the same path in the C-family. While the first version of the C++ compiler was more of pre-preprocessor or transpiler to C, in modern C++ macros have grown out of favor because the compiler now has better alternatives. At the same time C++ constant names have evolved away from from macros and now use PascalCase with ‘k’ prefix.
C# is also in the C-Family but .NET naming conventions don’t allow all caps even for acronyms (eg HtmlDoc). So clearly someone noticed that all caps wasn’t uniformly beneficial in the C-family.
Developers of Typescript have a choice, with Google using CONSTANT_CASE and the typescriptlang docs using PascalCase.
When I apply my principles to the question of naming constants, I would conclude:
P1 : Correctness doesn’t add weight for special naming for constants, but if there is a convention it should only apply when values are provably immutable.
P2 : Cognition suggests to only use all caps for exceptional behavior, since it it implies danger, but true constants are probably the safest thing out there.
P3 : Conformance is most applicable here for Java and C, with the long history of CONSTANT_CASE for a very broad in-group of code and developers.
P4 : Compatibility was probably misapplied or isn’t relevant.
Future of Concinnity
Where does that leave me?
I still chafe inside when I see all caps for enums, constants and especially the LOG member in a Java class. But I don’t fuss about it unless people ask me to rant about something for a few minutes. At this point it is so broadly and deeply ingrained in Java culture, that trying to change it would be transgressive. So I don’t spend my time debating it, but I do use it as an object lesson for the lasting effects of false cognates in programming languages. And I can use my principles to both reason about past decisions and imagine how I might make them differently. With Typescript (or a new language), I will argue for PascalCase, or at least against all caps for constants.
I still love code, cherish developers, and seek concinnity.
What sort of models, principles, or tenets have found most useful in your personal or team guidance?
Happy Coding!
Zeke