Managing risk in software projects - Six lessons learned from a recent MVP
These are my six picks of lessons learned and challenges we overcame from a recent software project. As Mike Tyson once said, "Everyone has a plan until they get punched in the mouth."
I've worked on a substantial software project for one of our clients for the last eight months. It was a great and fun project with lots of lessons learned, excellent teammates, and an incredible client. We hit our milestones and the final deadline despite unexpected challenges. In the spirit of sharing what we learned, this post summarizes what we did, what worked, and what didn't.
About the project
We set out to build a minimum viable product for a new online learning platform with custom native iOS and Android apps and a Node.js backend to support both apps and handle third-party integrations. On the DEPT® side, we added to the team over time, but it consisted of three iOS developers, three Android developers, two backend developers, one designer, and one project manager. The client, an owner of various physical product brands, was pretty new to agile software development, so we helped educate them on the processes involved throughout the project.
When I joined the project, we had already completed a discovery sprint for the client and had set the scope, requirements, and deadline for the MVP launch—all essential things to set us up for success.
Lessons learned
Lesson 1: manage expectations with thought and care
The first step to ensuring we hit the MVP launch deadline was to get a list of the main features (or epics) and assign a rough estimate for their implementation time. I made a Gantt chart to visualize this for the client, help us determine whether we could achieve our goal, and plan resource usage. However, we stayed flexible and adapted throughout the project. We work in an agile way, after all.
Unfortunately, that chart put us a couple of months after the desired deadline. One common practice is to add extra time to consider various parameters, such as:
- Feature freeze date milestone
- Final QA and polish
- Security audits
- App Store/Play Store review delays
- Holidays, PTO, and illness
- Unexpected technical challenges
This list is not exhaustive, but it illustrates that things can take a left turn in many ways. So, accounting for these is essential to keeping you honest about the needed time. By accepting more risk, you can significantly reduce the time. The client wanted this in our case, and we obliged. However, we only did this after negotiating some features as stretch goals and setting the expectation that cutting down the time will carry additional risk and may require us to punt features after launch.
Lesson 2: define roles and responsibilities
Starting a project with new team members, a client not overly familiar with an agile process, and no officially dedicated PM meant we were on our own in managing ceremonies. To at least get us started, we had daily stand-ups and some weekly meetings with the client to ensure we kept moving forward on features. While it was a rough start, we did get things done as most of the team was very experienced, but the solo Android developer needed help to keep up.
Eventually, one additional Android developer joined; a month later, a third joined. The client stepped in to help fill the need for a PM to get more structure, although this was a bandaid at best as this person also had other responsibilities, which meant they couldn't focus on keeping a nice backlog of features. It pressured the team to add tickets and determine their requirements and scope, which took away their work time. I tried to take on this role initially, but it did not scale as I had other responsibilities as a tech lead and one of the two backend developers. In short, my workload increased, and my productivity suffered.
This setup lasted a few months until the temporary PM left the client. We had a good flow going but needed a real PM to step in and take pressure off the team. Finally, the client agreed that we could bring one on. Doing their best to avoid upsetting how we worked, our PM took ownership of the ceremonies and provided a more structured approach to our processes. Once the dust settled, we were full steam ahead.
We had a typical set of agile ceremonies:
- Daily stand-ups lasted up to 15 minutes.
- Backlog refinement sessions were held twice per sprint and lasted 30-60 minutes. They require detailed features, well-defined scopes, and developers preparing estimates.
- Sprint retrospectives once per sprint for about 1 hour. They are essential to help streamline processes and celebrate wins.
- We held stakeholder meetings as often as needed but usually once weekly for 30-60 minutes. We usually had them without the broader team for efficiency and to avoid polluting their calendars.
- Design reviews once per week for about one hour. These worked great for us as the design was still evolving throughout the project. The whole team could discuss and fine-tune features and requirements based on what was technically feasible.
We also had a weekly internal developer-only meeting to discuss technical implementations and a meeting with someone on the client's IT team to help unblock us as needed. That last one was important as we used systems set up by the client, so we had limited permissions to adjust configurations on our own.
Lesson 3: be intentional with communication
Ceremonies gave us plenty of time for scheduled synchronous communication with specific contexts and topics to discuss. But, having options for asynchronous and ad-hoc synchronous communication was also crucial for our success. Err on the side of over-communicating. It's vital when working in a fully remote team.
While we didn't have official communication guidelines, we shared a sense of how we wanted to communicate. In retrospect, formalizing these guidelines would've been helpful, mainly as we added new team members.
Some examples of asynchronous communication are:
- We used direct and group Slack messages mainly to initiate synchronous huddles. We kept most messages in shared channels and tagged the relevant people to ensure the team was aware of ongoing conversations. Figma was our design tool of choice. Within it, the whole squad sent asynchronous messages (comments) to follow up on specifics and implementation details.
- Backlog tickets held not only the scope and specs of the feature but also comments and references to the design files.
- We had code reviews for the developers to discuss implementation details and keep us honest about code quality.
- Finally, we also had the occasional email for things that didn't fit any other mediums or involved people needing access to the above tools.
We had a few unspoken rules of thumb when it made sense to move from async messages to a huddle or scheduled meeting:
- Three or fewer involved: typically an unscheduled huddle.
- Three or more involved: should schedule a meeting, ideally on the same day.
- Does my question have follow-up questions?
- Is there likely some back and forth to figure out the answer?
- Is there an issue affecting a feature, and will we need to devise a compromise?
- Do I need technical feedback for a design decision?
Lesson 4: be flexible on tooling
As you might've guessed from how we communicate, we're big fans of Slack for productivity here. However, as much as possible, we work with the client and their needs, whether it's simply a preference on their end, established systems, or security and compliance reasons. In this case, we used Microsoft Teams. It worked, but we had severe connection issues. These issues caused communication problems and made it hard for us to stay in touch. Thankfully, the client agreed to move most communication to a separate Slack workspace.
Another tool we used based on the client's preference was Azure DevOps. Unfortunately, in our case, no one on our project team had experience with it. Similar to Teams, it does what you need it to:
- Tracking features and bugs in tickets
- Store source code using Git
- Build and deploy pipelines.
Due to our inexperience, it took us some time to get familiar with it. Considering all the customization options, we could have set it up better. However, it was too late to make significant changes when our PM joined.
While it's a bit out of my wheelhouse, we also used Figma for the designs and graphical resources such as icons and colors. I'm glad we did; it is a great tool to collaborate on a design. We all dropped in comments there, asking for clarifications and requesting changes. Cleverly, our designer set up a separate file for the design work ready to be implemented by developers, so we only had to go back and make changes sometimes.
In an ideal world, you want to pick your favorite tools. Sometimes what you have works out, and sometimes it won't. It's essential to recognize the latter and adjust as needed. If the client is willing, this may be easier, but be flexible and work with the client.
Lesson 5: allow time for testing third-party services
The project required some specific technologies. Specifically, streaming videos and storing content in a way that a non-technical person can easily manage. Like tooling, picking these services can make your life as a developer easy or difficult.
Additionally, as we were building mobile apps with a subscription feature, we were also at the mercy of Apple's App Store and Google's Play Store, neither of which is trivial to work with.
I won't give away the names of the services we chose here, but I can share some nuggets on things to consider. First, for any third-party service you need to use, take advantage of a trial and build a small proof-of-concept to test it out. It can inform you and the client which option works best for your scenario.
Second, some services might be optional but will likely help simplify the implementation and speed things up. If so, build up a good case for why the client should budget for that, too. It would likely have helped us out a lot with subscriptions.
Lesson 6: be prepared to compromise
As mentioned earlier, we set the expectation early with the client that we had taken on a significant risk with a tight timeline. It paid off when we hit a snag with subscriptions and notifications. For both, we relied on some members not directly associated with the project on the client's side. We lacked permission to make the necessary changes ourselves.
We were halfway through the project timeline when we finally configured subscriptions. I had hoped it would be one of the first significant features we had finished as it carried the most risk. But it got delayed beyond our control. We worked through all that and got it working because we worked with the client to understand the effort put into this and the cause of the delays.
We had to bump another technically complex feature, which put us at risk of failing to hit our milestones. So we talked it through with the client and compromised a bit. Instead of supporting native notifications on day one, we'd support email notifications. It also meant the frontend developers could avoid that feature almost entirely and instead focus on finishing up other, more important features.
To summarize, have a plan but adapt as the circumstances change.
In the end, we did hit all of our target dates. We've wrapped things up with a big bow and wrote a ton of documentation for future travelers. We impressed the client and made some new friends along the way. And isn't that the most important thing for any journey?