10 Lessons on Making Software

Giff Constable product management

It’s hard to avoid the agony on prod-eng Twitter about the basics of making software. It’s heartbreaking. Making software is hard, but we as humans do like to make it harder. Here are 10 lessons of my own about agile software, in case it is helpful. Some are high level and some are super tactical. Use only as useful for your own context.

1. Make software a continuous process

The rate at which the Earth moves around the sun shouldn’t control your software project. Months, quarters, years — these are useful constructs for planning, goal setting, and setting expectations, but the work is the work. We all know that it’s hard, complex, and inherently unpredictable; not just the coding part, but even more so the delivering value part. So the best way to handle this is to turn our work into a continuous process — continuous improvement of ourselves, continuous delivery of value to customers, and continuous evolution of our strategy.

I’ve experimented with multi-week sprints/iterations and not having sprints at all, and I’ve found 1 week to be the most effective structure. You start the week re-aligning, you work the week, you evaluate how you are doing, you take as much of the weekend off as you can, and then you get back to it.

Anything longer than a week and people start to confuse the sprint length with “shipping something” length. Those two should be completely disconnected. Sometimes you can ship something in two hours. Sometimes in 5 weeks. When your agile sprint becomes about shipping something, you end up in one of two modes: either work expands to fill the time (unproductive) or you end up in crunch mode desperately trying to get something done (recipe for burnout).

Both are bad. Instead, we need to strive for sustainable intensity. Finding that balance is hard — it requires a leader who models it, and it requires a team who demands it of each other. It’s easy for a team to slip down to the lowest common denominator — i.e. the pace of the least intense person on the team. That’s not something to be proud of, it’s not how you win in the market, and it’s an invitation for top-down, deadline-oriented control to re-assert itself.

Btw, credit where credit is due: thanks to my former colleagues Jeff Gothelf and Josh Seiden for putting the word “continuous” in my lexicon. It’s the right word.

2. Always have a team leader

At Neo (now Pivotal), my colleagues and I ran experiments to see how flat we could make teams. Surely, we thought, if you get a bunch of smart, motivated people together with a shared goal, they can figure out the rest for themselves. Turns out, not so much. The kicker is hard decisions. Not everyone likes ‘em. People avoid them, delay them, or get bogged down in debate over them. It’s remarkably helpful to have one person looking at the big picture and making sure the team is making decisions intelligently and quickly (both are equally important). And to be brutally honest, it helps to have someone feeling responsible for modeling “sustainable intensity” in order to avoid the lowest common denominator problem I mentioned above.

The leader does not have to come from any particular discipline, however they do need a few attributes. They need to feel a personal sense of responsibility for the success of the team (both output and outcomes). They need to foster a true sense of partnership and communication across the team. Ideally they help shape the narrative (the story and optics) of the team across the company and executives. They ensure — through clarity and repetition — that everyone understands the goals of the team and the reasons behind those goals. They need to have the backbone to ensure that hard decisions get made (regardless of who makes the decision — it doesn’t have to be them). They need to have enough experience to have good judgement. Lastly and just as important, they need to have the soft skills to inspire, help, empower, include, push yet not alienate, the team drive to that success.

What is a good leader? My favorite definition comes from Francis Frei and Anne Morriss’ book Unleashed: “Leadership is about making others better, first as a result of our presence, and then in such a way that it lasts into our absence.”

3. Build strong triads

At the core of a great software team is a great partnership between the PM, the lead designer, and the lead engineer. There’s been a lot written about this so I’ll be brief, but I did want to shout out my firm approval of the triad concept. They should be talking all the time, both informally and with their own meetings in addition to the all-team meetings — usually as prep for those. They should be totally aligned. They should know each other’s business and be able to pitch in for each other, even if imperfectly, if someone is overwhelmed or on vacation. To state the obvious, this means the lead engineer is not coding all the time, not even the majority of their time.

4. Look through and past the frameworks

Every single process methodology out there gets ruined in human hands. Agile. Lean. Design Thinking. Doesn’t matter. Sometimes they get co-opted in a subversive way to fit an alternate agenda (exhibit A: SAFe). Sometimes just misunderstood (“MVP”). The best approach is to remember the original context of the framework (agile was created in an engineering service delivery context), to look past the tactics to the useful underlying principles, and make them your own. In doing so, you will make mistakes like the rest of us, but from there you can learn and iterate.

My own approach is to set guardrails and then let teams diverge from there. To say, “here is our baseline of what good looks like — we recommend working this particular way (XYZ).” It provides reasonable team autonomy without creating a situation where a bunch of people are fumbling around in the dark.

It’s very useful to treat everything as an experiment, including how we work. That creates the space for continuous improvement, which can only happen through experimentation.

I remember at Meetup, Yvette Pasqua (CTO at the time) and I (CPO at the time) had a few teams that wanted to try mobbing. Mobbing is where a whole bunch of engineers tackle a coding problem together in real-time (as opposed to pairing or a single engineer working the problem and getting code review later). From the outside, it looks terribly wasteful. Indeed, we got some comments from non-tech execs wondering what was going on. But we were open to letting the teams try it out, and concluded that yes, there were times that mobbing could be tremendously helpful, especially around big coding decisions with downstream implications. There were also times when mobbing was not helpful and we expected the teams to know when to use it and when not to (“fun” was not the criteria). Our job as leaders was to create the environment where: 1) experimentation could happen; 2) experimentation led to actual insights and conclusions; 3) the experimenting teams had an obligation to share those insights with the rest of our teams.

5. Design an interruption culture (within reason)

This sounds weird, but once a team has code quality under control, the next-most unproductive setbacks often come from software engineers not asking questions as they implement a story. How does this play out? The PM and lead engineer write out the user stories as thoughtfully as they can, but something is missed. The engineer gets to a point in their implementation and thinks, “huh, did they mean X or Y?” Now if the engineer is like most engineers, they are chasing a flow state. The last thing they want to do is break out of flow to go talk to someone. It feels tremendously unproductive because it is!

Not interrupting flow solves a small productivity problem at the expense of creating a very big one. Quite often, when the engineer chooses X over Y, they’ve made an erroneous assumption which is now baked into code. It might get caught soonish, it might not. The rework is far, far more expensive than the break in flow.

The way to solve this is to convince the engineering team that they have to stop and ask the question. Be patient — this is a hard habit to build. You also have to make this easy to do. The PM, designer, and engineering lead should know every user story intimately. This means that the engineer has THREE people to hit up on chat, phone, video, a tap on the shoulder, etc. Don’t create a dependency on the PM. Do make sure that those three are highly responsive. If the engineers have to wait, if they are bottlenecked, they will stop asking and instead will go back to making assumptions.

6. Simplify the tools

In many ways, the agile community at large has ignored the very first tenet of the agile manifesto: “Individuals and interactions over processes and tools.” That’s why I’ve found it helpful to have a mindset of minimal viable everything: what’s the least amount of process and tooling that lets a team flow, without having too little.

I do believe in things like weekly planning/alignment meetings and I do think regular retrospectives are excellent for team health (as long as they are an honest, blunt, yet safe space). My biggest annoyance tends to come with tooling.

Call me old fashioned, but I believe that PMs should write, or co-write, developer stories. I think a big part of the PM job is bridging the strategy/customer worlds and the engineering worlds. You can’t do that if you’ve lost touch with the details on either side. That said, the PM only has to concern themselves with the details of what something is and why it should exist, not how it is implemented in code.

You need to prevent lower-level engineering tasks from overwhelming and confusing the higher level “what/why” tasks. Historically, this is why I’ve hated Jira — everything spins out into it’s own card, flooding the backlog. I prefer tools where the implementation todos stay neatly on the story card itself. Whatever tool you use, aim for minimum viable simplicity to improve the flow state of the team and keep people focused on what really matters: why something is being built!

7. Create escalation paths

While executives need to know who owns final decision-making power, I actually don’t love the idea that product teams have “final decider” roles for different issues. The members of the team should respect each others’ expertise, but there are times when someone is really concerned about a decision. A designer should be able to object to an engineering decision and vice versa.

Don’t put it to a vote. If it’s important enough, escalate it to someone above the team. Pull in a VP. If you have good VPs, it shouldn’t matter whether you pull in a PM, design, or engineering VP. They should know that their job isn’t to make the decision for the team, nor to back the opinion of whoever is in their function, but rather to ask the right questions of the team and help shape the higher level strategic or operational contexts so that the best (or least worst) decision becomes clear.

I’ve seen this work wonders for increasing the feeling of partnership on a team, yet also respecting the very useful right for an individual to pull the andon cord.

As a leader, the only way to seed this is to get buy-in across the PM, design and eng leadership, to create a safe space for escalation. Yes! It’s ok to disagree with your teammates, it’s ok to reach outside of the team for help. It’s not just OK, it’s expected of you! You need to repeatedly communicate this to the teams. You also need to train the VP level to know how to handle an escalation. One bad experience with a bossy or biased VP and the team won’t trust the process again.

8. For new teams, create a working agreement

There’s nothing like working on a team that just flows. Everyone is aligned around the what and the why, people can debate ideas safely yet respectfully, everyone is good at their jobs and can be counted on to deliver. When you get these conditions right, you can just fly. And it feels great.

It takes time to get into this state. A useful crutch to speed this along is to create a team working agreement. This is a living document (i.e. can be updated at any time) that everyone signs off on. It takes unspoken assumptions and gets them out in the open to be discussed, debated, and aligned around. Useful things to include: what are our working methods (weekly iterations? TDD? code review process? etc); what are our overlapped working hours (critical for distributed teams); and anything else that is important for spelling out what “good” looks like. But remember “minimum viable everything” — keep the doc as simple and clear as you can, and don’t let perfect be the enemy of good. Ship something together, and iterate as needed.

9. Don’t ask for permission — bake the right things to do into your work

There’s a laundry list of “best practice” working methods that many teams want to do and yet don’t seem to get executive permission to do: research, experiments, analytics instrumentation, A/B testing and tracking, internationalization, writing tests, and iteration.

Boy that list is depressingly long, isn’t it?

My basic recommendation is to not ask permission to do the work the way the team knows the work should be done, especially if you can keep the time investment efficient and reasonable. Of course, you can’t be in outright revolt, but a lot of teams can do more than they think.

For example, bake analytics or internationalization right into your stories. Don’t make them a later phase or a separate bunch of stories that can be punted to the icebox. Make writing good software tests (whether TDD or another approach) simply part of the process of writing code. You don’t ask permission for a sprint planning meeting, do you? So why do it for this other, also necessary, stuff?

Iterating a feature release, however, is one area where you do need executive support and cover, because the space for iteration needs to be created on the roadmap.

10. Build autonomy through earned trust and communication

Most reasonable people accept that, in the ideal, it’s a good idea to give a team a goal and the autonomy to figure out how to hit the goal. So why doesn’t it happen? The answer is simply trust. Leadership doesn’t trust the teams to make the best decisions. They’ve seen teams get in their own way, or chase their own agendas instead of the company goals, or any other myriad of mistakes. Sadly, the alternative — executive control — rarely delivers good outcomes either because the execs aren’t close enough to the data and feedback loops.

To get to autonomous teams, it takes two to tango. Leadership must be clear about strategy and goals. And the team has to not just respect the guardrails put in place by leadership, but constantly communicate, constantly check-in: “Are we still aligned?”; “Hey, you should know about these challenges.”; “Here’s our progress and confidence in hitting the goals we set.” etc etc. (see an OKR structure I like to use)

Some execs get confused between A) a team learning how to execute well and work together with B) a team ready to handle local decision-making autonomy. You can grant the latter while leaning in to coach the former.

By the way, the communication I speak of isn’t just something teams have to do for their execs. As a CPO, you are constantly checking in on, and reinforcing, alignment with the CEO and other execs. In a company, no one should run solo for a long time. So remember that communication is not a sign of lack of trust. It is a necessary way to build and reinforce trust. Don’t be a black box or you could lose the autonomy you worked so hard to gain.

Final Word

Of course there’s a mountain of other important topics when it comes to making software, but I hope the above list might be useful food for thought for your team.

Top image by Fabio Henning on Unsplash