How to create an engineering culture of cohesive teams and successful software projects
Modern software development has come a long way from the days of waterfall models and the mythical “man-month.” Developers no longer find themselves siloed away, solitarily writing code for whole features at a time. It has instead evolved into a collaboration of developers, designers, project managers and product owners working together to achieve a common goal. When everything is going great, it hardly even feels like work. Everyone works together better, and issues are quickly squashed.
It is in this modern environment that I introduce some practices and norms that have had a direct, positive impact on software development teams, both in terms of quality of work and team member morale.
Fostering a positive team environment
In my experience, the more positive outlook a team has on a project, the more successful it is. Even in a remote work environment, a little positivity can help in building high-performance teams and encourage team members. Without it, even the best-laid plans for a software project can turn into a death march when it comes time to deliver.
Allowing flexibility in scheduling and work hours goes a long way toward fostering a positive team environment. With remote work the norm, both managers and product owners should take a more flexible work schedule into sincere consideration for implementation among their teams, if they haven’t done so already.
Also, take into consideration all the typical benefits that remote working brings (i.e., improving work/home life balance, greater flexibility with hours, higher morale). If you are still not convinced, let me share this tidbit: the Toy Story franchise would have been likely limited to just a single movie due to a catastrophic data loss event during production of Toy Story 2. Had it not been for a remote worker’s home database server that held a copy of the data that was lost, the studio likely would have had to cancel the movie. The whole story around it makes an interesting read and would recommend checking it out.
Regardless of where your team is on the remote/in-office spectrum, I encourage installing the mobile version of your collaboration applications (Teams, Slack, Outlook, JIRA, etc.). It adds versatility in when, where, and how you communicate with other team members. If I'm on a meeting call, and the environment suddenly becomes too noisy or distracting and moving with my laptop is not an option, the ability to transfer to my mobile device and move to a calmer and quieter location has been a huge help with my remote work.
To have a successful conclusion to your project, it’s important to ensure everyone on the team has a great start. Before you break ground and start to write code, setting aside time in the beginning to groom and set up a proper backlog can pay off greatly once you are up and running. Start off with a clean slate; avoid the temptation to save time by trying to shoehorn in stories for an earlier or similar version of the application you are working on.
This is also the time to set up sensible sprint norms for your team. Now when I say sensible, I am leaving some room for interpretation here, but one suggestion is to do away with the practice of named sprints/iterations (unless of course, you are simply numbering them). I understand the novelty of naming them after your favorite movies or Marvel characters, but it provides little value when trying to do anything with them. Telling me a story was completed somewhere between sprint Jaws and sprint Willy Wonka tells me nothing. A story completed between sprints 9 and 12 gives me a clearer period to reference.
Regarding user stories, put in as much effort in writing and maintaining them as if it were the source code for the feature it was describing. Descriptions should be more than a simple echo of the story title, and acceptance criteria should be present and clearly defined. I’ve always felt that the “developer hit by a bus” analogy is a great and perhaps a bit too morbid of a way of putting how important it is to ensure that if a story needs to change hands it can be quickly picked up no matter who picks it up.
Now, I understand that a counter argument could be made against this level of upkeep on a backlog saying that it requires a fair amount of time and effort to keep and even takes the developer out of the equation on design decisions. However, this is still a fair trade-off if it is done collaboratively, since every developer still has a way to contribute to the design decisions of the product while keeping everyone better informed and further engaged with the project in the process.
Hold regular check-ins with your teammates and manager(s), as this is another terrific way to foster a positive team environment and keep communications flowing. I’m not talking about the ceremonies around regular stand-ups; rather something like a bi-weekly or weekly check-in style meeting where everyone can safely raise issues and get help in real-time. Or this check-in could simply give some much-deserved recognition and praise to other team members for helping or going beyond in their day-to-day efforts. Statements like:
- “I would like to give a big thanks to Alice for the insight she gave me and helping me solve a bug that was really giving me trouble.”
- “Just wanted to recognize and give a huge virtual fist bump to all of our developers for their work in this sprint for not just meeting but exceeding our targeted velocity for this sprint.”
- “While I understand that our story estimates are made in good faith, it does feel like we tend to underestimate the effort needed often. Let’s commit to spending more time researching stories prior to estimating them for our next iteration.”
The next concept may sound a bit counterintuitive, and I’ll even admit at first, I found myself a bit skeptical of how Failure Tolerance could help. Naturally, “failure” has bad connotations, and as developers we strive to ensure that every deliverable that goes out the door is of the highest quality and as bug free as possible. But that is not the kind of failure we are talking about here.
I was brought on a project once to help catch up on feature development for a product update that was falling behind and in danger of missing the delivery date. The existing project manager wanted to make sure as few stories as possible were carried over from sprint to sprint. In doing so, the PM viewed nights and weekends as an “emergency time bank” that we could use to help get a story over the finish line. Now, it was bad enough that this created a development crunch at the end of every sprint due to the volume of work needed to catch up on; it also took a big toll on morale. What was even worse was it eventually led to a senior lead developer quitting the project and resigning from the company.
After a slight change in staffing, our new project manager was onboarded, and one of the first things he decreed was we were going to tolerate failure with our stories. What did he mean by that? Firstly, that the practice of tapping into nights and weekends was a surefire way to create developer burnout and was hurting our morale more than it was helping us complete stories by end of sprint. Secondly, if a story was not completed by end of sprint, we would consider it “Failed” instead, and had a discussion on why we thought it failed and what the remedy should be (Do we still carry it over? Does it need further refinement? Do we send it to the backlog and pull in something more important or relevant instead?).
Granted, under this new system, our velocity took a hit for the first few sprints. In forcing us to address why the story failed instead of simply making sure “to get it done,” it allowed us to see the root of the problems that caused the failure in the first place. Things like overcoming technical debt, inadequate or biased story estimates, choosing to write “quick and dirty code” to close out the story were some of the repeat offenders. Once we could properly address and mitigate those issues, we achieved a greater velocity even without the crunch. Also, having our nights and weekends back was a huge improvement to our overall morale as there were no further departures of developers from our team.
Staying in scope and relevant
Lastly, a clearly defined scope of work is as important as having a relevant (as in recent) tech stack. If you treated planning your project like planning an overnight hike, you would make sure you had a map handy with your route and destination plotted out. You would have all the supplies you need (water bottles, tent and sleeping bags, etc.) and in good condition.
Continuing with our example, it’s easy to see if you were to expand on the duration of the hike, you would likely need to take on extra supplies to mitigate any newly incurred risk. Likewise, using old and outdated equipment could cause unforeseen headaches down the road. When it comes to project planning, consider if your frameworks/libraries/packages included need a version update. This should be obvious, but it’s important to perform any updates having fixes for security vulnerabilities in your code.
Allowing scope creep (or not having a sense of scope at all) to go unmitigated will certainly come at a cost of time, resources and quality. Not having a definition of “done,” or some kind of designated endpoint, can make it difficult to design a component and complicate maintenance activities around it.
That’s why not only is it best to have an idea of scope clearly defined at the beginning of your project, but also to have procedures in place to assert the impact of scope changes along with risk mitigation. If changes to scope are asked, then the cost of the change will be known ahead of time and you can decide whether to go ahead while minimizing the risk to timeline, quality, and cost.
Also, always take the time to assess the stability and sustainability of your code base. All things break down on a long enough timeline in the real world, and code is no exception. Periodically reviewing your code (and tests, too) will give you some insight on what looks good to keep, what can be improved upon, and what can be retired. Think of it as spring cleaning for your code base. Not only will your product improve as time goes on, but your engineers will be more engaged and gain an expert level understanding of it.
Having a great developer/engineer culture can help strengthen teams and empower leaders. Everyone works together and communicates better when they find themselves part of a team environment that is highly supportive of its members. When project goals are clearly defined and upkept through normal processes, it will help fortify a higher standard in quality of output in deliverables while navigating any changes in requirements. Allowing room for improvement and asking the occasional “Can we do this better?”-type questions will only make this better, not only for your project but for the development of every professional on it.