Like any group of individuals working together, developers all have their own way of doing things. When you add different programming styles, personalities, priorities, and opinions into the mix, there’s bound to be a time when things don’t go as smoothly as they should.
There could be any number of reasons for conflict. The key to diffusing it is understanding both sides and learning to take an approach that satisfies everyone involved.
The perfectionist vs the pragmatist
The battle between meeting exacting standards and shipping a product as soon as possible is one that spans all industries and time immemorial. There will always be a group of people who believe the attainment of perfection is more than a grand idea, while there will similarly be a group of people who believe that getting a product into the hands of users is the best way to begin iterating.
Beyond simply wanting to put out work they’re proud of, perfectionists make some good points about the costs of paying for technical debt down the road, including reworking sloppy code and maintaining a code base that started out at a deficit. Oftentimes, tests are skipped in an effort to save time, only to have quality issues pop up that would have been easily caught had those tests been in place.
The pragmatists out there are quick to say “don’t let ‘perfect’ be the enemy of ‘good enough’, or we’ll never get anywhere.” And in a world where things move fast, many users would be thrilled to have new features, even if the code behind them isn’t pixel perfect. Polishing takes time, and prioritizing speed is how most users see the work the team is putting in.
So how do you reconcile the two? For starters, establish the “must haves” and “nice to haves” before release, as well as the quality and speed metrics your product needs to meet. If you’ve got a good UI and solid performance, the code under the hood can take a back seat in order to have an MVP to release. Putting both pragmatists and perfectionists on your code reviews to get an idea of what each side sees in the work that’s being put out can help determine a release path moving forward.
The maintainer vs the purist
For any project that starts out on legacy code, you’ve got another couple of camps to work within: the dev who is fine with maintaining the code in place, and the purist who thinks it’s always better to start from scratch and do things the “right” way.
Maintainers would rather avoid disruption in existing systems, pointing out that rewriting the code can introduce new bugs, break components, and can’t always account for downstream dependencies. They’re also a time-consuming process, which takes away from building and improving on existing features. Add in that changing languages and structures can alienate the existing devs, which ultimately strips the team of institutional knowledge, and there are some good arguments for maintaining what’s already there.
When it comes to a full rebuild, there’s the argument that building on legacy code will ultimately lead to an increase in technical debt, as older systems feel the constraints of new features and optimizations enter into the equation. Security and maintainability can also be a major concern with legacy code, as can attracting talent that hasn’t worked with the increasingly more dated stacks.
It’s rare that a company has the funds and resources to both maintain the legacy code with one team and start a full rewrite with another (but man oh man, can you imagine?!), so a balanced approach is necessary. When it comes to the original code, prioritize rewriting the most problematic components first, followed by prioritizing the refactoring, testing and documentation of it. This way, the most glaring issues are dealt with first when you’re doing incremental rewrites, and the code that’s left will be in a good place for whichever team tackles it down the road. Maintaining both the old and new code as you run them in parallel, while eventually hiding your old system behind an API to avoid losing it all, can appease your devs and help the team grow into the new system together.
The lone wolf builder vs the consensus builder
Who among us hasn’t worked on a project with far more than the essential number of stakeholders, and said to themselves “I’m the one who knows how this works, why won’t they trust me?”, only to realize down the road that there was a very good reason for those requests?
The lone wolf has a tendency to feel like collaboration slows down the development process, bringing everything down to the lowest common denominator and taking the wind out of the sails of a fast-moving ship.
The consensus builder is more prone to feeling like the lone wolves won’t consider other peoples’ perspectives, and don’t want to feel blocked by the decisions made by a single person. Additionally, letting a single person run free can lead to missed requirements or low standards because there’s only one voice in the room when decisions are made.
Ultimately, balancing the two can lead to some really strong development practices and get buy-in from the whole team. Collaborating on architecture standards and leveraging the wisdom of invested stakeholders can provide some team-level structure. Assigning certain components or projects to lone wolves gives them the autonomy to work within the team structure, while checking in on their progress and having working groups for only specific problems keeps everyone on track.
Clearly communicating requirements, setting common standards (with flexibility), and ensuring everyone feels like their voice is heard are important in balancing different personalities. In the end, most debates happen because all parties have a vested interest in the quality and success of the project, so giving that aspect its due respect means also respecting those voices.
In the end, some disagreements are inevitable. Aim to channel that productively instead of trying to eliminate the differences entirely, and you’ll be working towards creating a more cohesive, invested team.