Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.
— Melvin E. Conway
A very sage-like programmer that I had the fortune of working with would often say that most people don't realize that software development is a human endeavor. The more experience I get, the more I can see how true this is. When reading code, developers often leave an imprint of their experiences. After all, code is an expression of the author's understanding of the requirements.
Composition and Collaboration
By far, one of the most pleasant patterns that I like to see when reading code is the heavy use of composition and/or collaboration. Collaboration is usually identified when objects make requests and fully trust the downstream object to complete the task. Interfaces are used to define the contract and everyone is in agreement.
In order for these patterns to work, there needs to be a lot of trust between the developers within the organization. In these kinds of code bases, it usually implies that:
- Cross-team communication is healthy
- Team-members are encouraged to work together
- Organizational trust is high
- Teams are self-organizing
God-objects
The opposite of collaboration is the "god" object. Usually code bases containing a few objects that exert control over the system is a tell-tale sign that the organization has a top-down approach. This is not necessarily a bad thing because in certain situations and scenarios, there needs to be some amount of orchestration. If the "god" object is encountered, it could be a sign that the team involved could be important, but needs to provide information and procedures to your team on a need to know basis.
A red-flag is if the "god" object patterns are pervasive in the code. The code usually ends with a top-down approach where leaf objects in the object graph are fairly dumb and only do as they're told. In these kinds of code bases, it usually implies that:
- Cross-team communication is done by management
- Developers freedom is probably low
- Micro-management may be just as pervasive
- Organizational trust increases by position
Defensive Guards
Code that is overly defensive is usually a sign of lack of communication. This could be at the team level or even within a team. If two developers do not communicate or even have a shared contract, it naturally follows that in order for their code to interoperate, they need to verify and validate every input at the boundaries.
As an example, when receiving a web request, it is a best practice to verify and validate the request provided by the user. This is because we cannot fully trust the end-user. However, if this is reflected within the internal code, this usually implies that:
- Internal team lacks communication
- Developers work in isolation
- Management may be hands off
- Organizational trust is non-existent
Indirect Expressions
This is a pattern that I've only encountered once in my career. Honestly, I have no idea what it means, but it was interesting. The code base was littered with requirements that were specified in the negative. For example, compare the following:
if (color is Red) { ... }
if (color is not Blue or Green or Yellow or White or Black ...) { ... }
It was one of the most confusing code bases I have experienced. The only thing I could understand from this pattern is that most likely the requirements were expressed in an indirect way. If this is reflected within the code, this could imply:
- Requirements are not clearly expressed
- Developers are not challenging the requirements for clarity
- Management may not have the information needed to support the team
- Organizational understanding about the product is not clear
Excessive Comments
Code bases with a lot of comments usually reflect the the developer's ability to communicate. When a developer can express the meaning and intent of the requirement directly with code, then comments become redundant. Compare the following:
int i;
for (i=1; i<=100; i++)
{
// number divisible by 3 and 5 will
// always be divisible by 15, print
// 'FizzBuzz' in place of the number
if (i%15 == 0)
Console.WriteLine("FizzBuzz");
// number divisible by 3? print 'Fizz'
// in place of the number
else if ((i%3) == 0)
Console.WriteLine("Fizz");
// number divisible by 5, print 'Buzz'
// in place of the number
else if ((i%5) == 0)
Console.WriteLine("Buzz");
else // print the number
Console.WriteLine(i);
}
Enumerable
.Range(1, 100)
.Select(AsFizzBuzz)
.ToList()
.ForEach(Console.WriteLine);
...
private string AsFizzBuzz(int i) =>
i switch
{
var x when x.IsDivisibleBy(5) && x.IsDivisibleBy(3) => "FizzBuzz",
var x when x.IsDivisibleBy(5) => "Fizz",
var x when x.IsDivisibleBy(3) => "Buzz",
_ => i.ToString()
};
...
public static class IntExtensions {
public static bool IsDivisibleBy(this int numerator, int divisor) =>
numerator % divisor == 0;
}
In the second case, for the most part, the core domain of the code reads like English. It would be redundant to say when x is divisible by 5 and is divisible by 3, then return FizzBuzz as the code expresses that clearly. Code with excessive comments could imply:
- An abstraction is missing
- A knowledge gap exists
- Requirements need to be clarified
- Implicit dependencies are creating confusion
Chaos
There's probably no need to explain this, but if the code base is completely chaotic, it's a tell tale sign that the organization is either:
- Experiencing tremendous growth
- Experiencing a downward spiral
Interpret it how you may, your mileage may vary.
Conclusion
These are some of the observations I've made over the years. What have you noticed when reading other people's code? What does it tell you about them or the organization?
References
- "Conway's law" (2022, January 3). In Wikipedia. https://en.wikipedia.org/wiki/Conway%27s_law
- "Fizz Buzz Implementation" (2022, January 27). In GeeksforGeeks https://www.geeksforgeeks.org/fizz-buzz-implementation/
- "Formulae" (2009, May 16). Guillaume Piolle https://commons.wikimedia.org/wiki/File:Formules.JPG