TL;DR: I’m putting together some experiences that I feel are potential contributing factors to burnout as a software developer. These are the factors that I believe can drain your energy when it comes to building software, and I’ll be sharing ways that I think can be done to prevent them. At the time of writing, I have been a full stack web developer with over a year of experience in building software. So this might look like a newbie’s perspective for senior devs reading this. I’m hinting at this being the first part of a to-be series of posts on the same matter.
The workload as a developer is entirely different from the repetitive and redundant work that some may have imagined. This might depend on the work culture of each company. Some companies focus on specialization and assign roles that work on specific areas of the software system, while others practice full-stack development, which requires individuals to be a jack of all trades and perform multiple specialized roles. However, there are certain challenges that I think are common to this kind of work regardless of culture and practice. I am going to share some of them that I personally believe can lead to developer burnout and also share workarounds on how they could have been done better.
For starters, burnout is a form of exhaustion caused by constantly feeling swamped. It happens when we experience too much emotional, physical, and mental fatigue for too long. In many cases, burnout is related to one’s job.
1. Massive PRs = Review hell
While there is no definite measure of a healthy size for a pull request, one can definitely tell if a PR would be a pain to review based on the number of files and lines of code changed and the scope it covers. Ideally, pull requests should be concise and only include the required and desired changes.
There are developers who, instead of just changing the lines of code needed for the feature implementation, also proceed to format the entire file on which they made their changes. The result is a lot of lines of code that have been refactored. This is not ideal, as it makes the code harder to review due to the sheer number of changes that a reviewer would need to pay attention to. These high numbers of changes also increase the cognitive load of the reviewer, as more attention to detail is now required to ensure that all the refactored code does not introduce bugs and regressions.
“Cognitive load refers to the amount of mental effort being used in the working memory“
One potential cause of massive pull requests is when feature specifications have not been sufficiently broken down into manageable tasks. Examples of such tasks include changes that span multiple modules and classes.
Ways to avoid having massive PRs
A. Separated linting from implementation.
Linting changes is an example of this issue. Most software organizations use a code linter/formatter to maintain code quality. Applying a linter to your processes promotes adherence to specific software formatting standards and ensures uniform formatting across the codebase. If you believe that formatting an entire file is necessary, you can create a separate PR specifically for the formatting changes. This way, you can separate the concerns into two PRs with different objectives.
B. Design and analyze first rather than diving quickly into implementation
A common mistake in programming is underestimating the complexity of a task. This is something that no one is immune to, but it can be prevented. People often assume that if something is easy to imagine, it will also be easy to implement. However, this is not always the case in software development. Time and time again, you may find yourself discovering complexities related to your task’s goals that make you reconsider your commitment to the task’s deadline.
The solution to this is to plan your implementation based on what is necessary and spend time researching the scope and areas where you need to make changes. To take it a step further, you can design test specifications after conducting your research. Doing this provides you with a heightened awareness of the complexity of your changes and their potential scope under the hood.
C. Keep it simple
If you are asked to change a listed value of static translations, do just that. Don’t get sidetracked by editing CSS styles just because you feel they are not aligned with your linter’s formatting. Also, make sure to review your staged changes before committing them. Do what is asked, commit, test it, and open your PR for review. Avoid including changes that are outside the scope of the request. Refraining from refactoring variables just because you think the name isn’t ‘clean’ enough. Keeping your PRs simple makes them easier to review.
2. Gigantic classes
Speaking about massive PRs, classes with over 500 lines of code come second on the list. I have worked with legacy code that has classes on the front and back end perfectly fitting this criteria, and I must say it has not been very enjoyable. These classes are full of global variables with values that are mutated throughout the class, and they are also filled with nested invocations of private functions. All this effort is made in the name of keeping related logic grouped together. While I acknowledge the advantages of this approach, it doesn’t necessarily make development easy, as you have to navigate through a lot of code.
My assumption regarding what causes a class to become bloated and grow to the size of a 500+ line code is that developers didn’t know where to draw the line and didn’t care enough to consider that the class was getting too large and needed to be split into smaller parts.
Clean code always looks like it was written by someone who cares.–Michael Feathers
Ways we can avoiding creating gigantic classes
A. Knowing when to draw the line
Proper research and planning before implementation is one way to achieve this. At first glance, one can clearly tell if a class is large. You just need to consider whether it’s wise to add another block of code to it or not. This judgment can come with experience, but it’s an important step in avoiding bloated classes. You can read up on the open/closed principle to learn how to add new implementations while avoiding bloating your classes at the same time.
B. Look for implementation patterns that you can use
Over the years, many patterns and designs for building software have been developed and proven to address implementation challenges. Applying patterns to your implementation helps spread and break down your code into smaller, manageable parts. Learning and leveraging these tested solutions can make your code easy to read and understand, while also providing the advantage of being easy to maintain.
3. Delayed reviews
In my experience working as a developer, it’s not uncommon to be working on two things in parallel at the same time. Most of the time, I would be handling one implementation task while also keeping tabs on and reviewing two pull requests. You may think of this as counterproductive since it divides my attention among multiple tasks. And you’re right, handling a lot of tasks and reviews can lead to delayed reviews for developers who are dependent on me.
The reason this is on the list is that delayed reviews slow down the delivery of features or bug fixes. The burden of this delay usually falls on the person who is dependent to the review or approval of their pull request.
How can we improve the review process
A. Rank reviews based on urgency and importance
In this line of work, knowing the things to do first is somewhat a skill. One method to do this is by using the Eisenhower matrix. When your requested change for the PR has been made by the author, the best course of action is to immediately go back to reviewing it if it’s both urgent and important.”.
B. Be mindful as a reviewer
Understand that when you assign yourself as a reviewer of a PR, you’re now a contributing factor to the time it takes for the tasks to be completed. This means that someone now has a dependency on your work, and they’re waiting for your approval/response. Additionally, don’t take code reviews personally. Avoid involving emotions or personal preferences and aspire to contribute to the effort of releasing the feature.
C. Develop your own review process
I am pretty sure that every development team has its own way of handling review processes. However, there are limits to how effective they can be. As you take on more reviews and gain experience as a reviewer, you will realize that there’s room for improvement as new challenges are discovered. When you reach this point, I believe having a personal way of conducting reviews becomes important, as it allows you to tailor the process to your liking and preferences. You may think that this could slow you down, as adding more steps to your current process will take more time to complete a review. However, the idea behind developing your own review process is not to dogmatically adhere to an agile process, but rather to make our processes and adhere to them in order to make ourselves agile. By realizing this, I can confidently say that having your own way of conducting code reviews is a good thing.
Conclusion
Among the contributing factors to a developer’s burnout are having to review massive pull requests, maintaining/implementing new features on code bases with gigantic classes, and experiencing delayed code reviews.
To avoid having massive pull requests, one approach is to separate changes for linting from actual implementation and keep the changes in the pull request simple. Another way to optimize your approach and reduce the occurrence of massive pull requests is to analyze the problem first, design your solution, and then proceed with implementation.
The size of classes with extensive scopes can be reduced by having empathy as a developer and considering whether adding your code on top of an existing class is still the right thing to do. Utilizing programming design patterns can significantly decrease the creation of large classes by breaking down and decoupling code into smaller and more manageable parts. Applying SOLID principles can also prevent existing classes from becoming excessively large.
To address delayed reviews, it is important to prioritize and complete review tasks based on their urgency and importance. As reviewers, we should be mindful of our responsibilities and acknowledge that someone is dependent on our response and waiting for it. It is crucial not to take code reviews personally and avoid incorporating personal preferences that go against agreed-upon best practices, both for the author and the reviewer. Lastly, having your own review process holds value as it improves the review process by addressing emerging issues discovered while handling code review tasks, and it also shapes your approach and way of conducting reviews.
With these factors in mind, we can enhance the developer experience for those we work with. It is important to remember that working in a software team is a collective effort. Our code only holds value when it is shipped into production, and our fellow developers play a vital role in helping us achieve that. Therefore, we should strive to reduce burnout factors and embrace healthier practices.