I am a software engineer and a TDD practitioner in my company. My manager is also responsible for coding (if needed), and managing his engineer subordinates.
I got into a few heated debates with my manager regarding the use of software design patterns recently.
Frequently when I am tasked to implement a feature and code, I am challenged by my manager during code reviews for my use of common software design patterns. He thinks it is unnecessary and one should code as ‘straightforward’ as possible. I put quotes on straightforward, because we seem to disagree what the scope of a ‘feature’ should be. In many cases, the feature is not as ‘straightforward’ as my manager thinks and require the use of design patterns to separate responsibilities and minimize impact to the (mostly untested) code base.
There are two common use cases I will apply design patterns to solve a problem:
Implementing new features that require changes to an untestable, legacy class
- I solve this by applying a decorator to the original interface so that the new code I write can be easily unit tested and make sure my code integrates well with the legacy code.
- His response is that he finds it confusing having multiple implementations of an interface and he thinks creating a new interface with another name (with the same members) is the way to go. However, this breaks the dependency inversion principle as the high-level logic is now changed to suit the low-level details.
- I tried to reason by explaining that I am trying to minimize risk by minimizing changes to untested code, plus my design is easier to unit test and to be proven correct, but he thinks I am over-engineering stuff.
Tackling uncertainties by interfacing the unknowns
- Quite often the requirements for a new feature is not thought out thoroughly. Faced with deadlines, we have to leave ‘holes’ in our code in order to let the business make up their mind without impacting progress.
- For example, I might be implementing an interface to retrieve a runtime value from some unknown data source, which I implemented a decorator which takes an IRuntimeValueProvider to retrieve it during runtime. I can leave the actual implementation till the decision is made without impacting other business logic.
- The manager thinks I am introducing interfaces with names he is not familiar with such as IHttpContentFactory, IHttpClientProvider. He is still not satisfied with my explanation and demonstration that these factories serve specific purposes in constructing components and provide a seam to extensibility.
- In the debate of my usage of IHttpClientFactory to abstract the construction of HttpClient away from its consumers, he specifically mentioned that he thinks the consumer should be responsible for constructing every implementation detail, which would lead to construction logic being spread all over the place.
We got into a few arguments due to similar design meetings. In one heated argument, I was even told that "[he has] been programming for more than 20 years!", implying I should not even dare to question his authority.
What I tried to mitigate this problem
- Written detailed comments on some not-so-obvious components why I needed those and what purpose do they serve
- Passively demonstrated that my design is much more adaptable to changes
- A component that I built has significantly lower bug count than most other components
- Correctly anticipated a change in requirements, which led to minimal code change necessary to fulfill that requirement
- Addressed his concerns upfront when I am challenged by explaining my thought process and reasoning
- However, I am still reprimanded for over-engineering code.
- Informally discussed this with my colleagues and asked for their opinions in private
- Most of them appreciate my well-reasoned approach and one even mentioned he learned a lot from me. Most of the engineers like to discuss their coding problems with me, because I can usually give them useful advice or point out something they might have missed
- Holding informal presentations to educate my fellow engineers (and manager) on why I apply a design pattern here and there
I do not want to sound arrogant by saying my coding skills is absolutely better than my manager’s, but I have really run out of ideas to convince him, even after the demonstrations, the explanations, and the objective results being shown to him. On one hand, I would like to deliver bug-free, well unit-tested and designed code. On the other hand, I keep getting criticized for my design does not feel good and has certainly ‘complicated’ my code by forcing it to the ‘approved’ way.
How can we find a middle ground?
Since I am getting a lot of comments mentioning about my dogmatic, even over-zealous attitude to follow software engineering principles, I think I should clarify:
I am not trying to be dogmatic nor overzealous. I do care about people reading and understanding my code, whether they use/understand design patterns or not. I asked for feedback from my colleagues during code reviews if they understand my code or not. They said they do and they understand why I design a component in such a way. In one occasion, my use of design patterns helped centralize the configuration lookup logic in one location versus having it spread out in dozen of locations, which has to be changed often.
To be honest, I am quite disappointed to see so many responses with a strong stereotype "Why you engineers can't think like managers". At the end of the day, every thing can be said to be over-engineering: Why mark fields as
private instead of exposing everything, why unit test if no user is going to execute just a single unit, etc.
My motivation in use of design pattern or in fact any software engineering is to minimize risk and bring value to the company (for example by reducing the unnecessary spread of logic in the code base, so that they can be managed at one single location).
Sorry if this sounds like a rant. I am looking for answers along the lines of 'how can we find a middle ground' instead of some condescending answers like 'engineers think they are smart by applying design patterns which no one understands'.