For those of you who have been on well-functioning teams that use stories (like Jira) and PRs, I'm wondering how you incentivize small PRs.
Our org traditionally estimates a product-oriented story with points, and then had one feature branch, which goes through QA and merge, at which point the story closes.
I think this causes all sorts of distorted effects, in that it simultaneously causes larger PRs, and also disincentivizes responsible coding practices that could grow the PR even more.
As these stories are product-focused, it's hard to break down the story into smaller stories, as this involves anticipating the tech work before starting, and makes it confusing to product folks if the stories start getting less product-focused.
I suspect it would be better to allow multiple PRs per story, or to otherwise push back on product? Should stories always be sized to accommodate one small PR?
Depends a lot on the domain. In some domains it’s possible to slice even product stories super small. This usually takes a lot of skill from the person writing the stories. But if you can get the typical story size down to “about half a day” it handles a lot of this problem.
What I usually do instead is multiple PRs per story & hide work behind a feature flag or something until it’s ready to show people. This doesn’t require a fancy feature flag framework or anything, just a hardcoded Boolean or code comments.
> I suspect it would be better to allow multiple PRs per story
Yes. Stacked PRs are one way to accomplish this. Another would to have the dev write smaller tickets for PRs and then PRs are attached to tickets from the tech team.
Anything that relies on product understanding how to write a ticket that is the right size & scope for the dev team to create good PRs is doomed to fail.
- Have engineers write most of the stories, as they are better at slicing things down to PR size.
- When product writes the stories, make sure we align on engineering approach as part of the pointing process. If something should definitely take multiple PRs, we call it out then.
- Heavily use feature-flags so that it's easy to have more than one PR for a story, as deployment to prod and release of a feature are decoupled
- Have a culture where it's totally cool to ask someone to break up a large PR into smaller parts.
Stacked diffs sound like what I sometimes do with PRs, where I branch off a previous branch. merging/rebasing from main would cascade through. It's a drain on QA though if they feel like they have to manually qa each PR, as opposed to testing the entire bundle of PRs at once.
My advice to you (at least what's worked for me on several reasonably well-functioning teams using stories and Jira in a similar manner as you have described:
Decouple users stories (customer/product outcomes) from tasks (units of work needed to achieve those outcomes). Jira is designed pretty well for this, since you can have sub tasks attached to user stories.
This works better when your user stories _are_ actually defining outcomes -- for example when you have stories like "User admins can filter jobs by category" and not "Build a category filter for the job search." The first can usually be successfully defined by a few acceptance criteria, whereas the second starts to get weird since you're focus is more on what it will take to build the thing vs. what is the result you want your customer to see.
With your outcome defined in the story you can define any number of implementation tasks it will take to achieve it. If you're a cross-functional team and you do "vertical" instead of "horizontal" splitting then you're sure to have a few coding tasks on the front end ("add the filter component to the search bar", "update the backend client to pass the category id as a query parameter") as well as a few on the backend ("update the API to accept the category parameter", "add the category param to the repository query service"). You probably have some non technical tasks too ("update the help documentation", "update the OpenAPI spec").
We almost never (save for absolutely trivial user stories) have a PR attached to the story but instead have PRs attached to sub tasks. If you're doing trunk based development with continuous deployment and feature flags, you can and should be shipping many PRs. Just yesterday I was in the middle of a task at the end of the day and decided to cut the PR where I was and split the task in two on the fly, since it was easier for me to ship the code like that and easier for my team to review it.
We do story grooming and estimation and all that -- but only for user stories. Tasks are the domain of the humans doing the work and they are meant to be flexible and even disposable. We usually have a session at the start of work on the user story when the engineers working on it align on the solution and then break the work down in to sub tasks, but these naturally evolve as the work progresses. I should had that multiple tasks invite collaboration instead of one story per engineer.
Lastly, I've found you have to preach the virtues of small PRs to your team and usually convert a few stragglers who don't see the value. I try to practice what I preach (i.e. by keeping my own PRs small) and also make a big deal out of it in retrospectives -- i.e. point out how painful the review process is with large PRs, usually entailing many rounds of comments and changes -- so that people quickly become believers if they are not already.
As a last point I try to encourage the value of "PR reviews are your top priority at any given moment" since every second a piece of code sits unreviewed adds to your team's cost of delay. There's a virtuous cycle here where smaller PRs lead to less painful code reviews lead to greater willingness to spend 10 minutes (vs. an hour) doing a code review, which helps really get PRs moving through.
I think PR stacking is great but I also find it's not as important if your PRs are getting reviewed and approved faster than you can write the code for your next PR.
(I didn't realize I'd write so much here, I forget that it's actually kind of a big topic that's built on a variety of different practices that all start coming together at some point when you get in a groove.)
Thanks! How do you integrate QA and merging with that? Do you QA and merge each subtask PR independently? Or do you leave all subtasks unmerged in favor of QA'ing the entire story, at which point you merge all PRs?
This is where feature flags come in. When you use feature flags to support development you ship code straight to production — but your work is hidden behind that glad while development is under way. So in practice you merge your PRs immediately to master (and deploy).
Essentially you’re decoupling release from development here. This supports any number of QA practices. (We don’t have dedicated QA at the moment and instead have a biweekly “mob QA” session where we do a group deep dive into our current work.) We will capture most small fixes and improvements as sub tasks on the appropriate story (or file a bug ticket if the story was already done and we discovered a new issue.)
As a result of the above we don’t use long lived feature branches which become painful and slow, process wise. We just merge immediately after review. (Unmentioned but this is of course supported by automated testing and continuous deployment.)
Thanks, this is super helpful. For us, we are pretty heavy with manual QA, and for work that can't be wrapped in feature flags, we'd still have to test a fair amount before merge, but at least it wouldn't have to be for every PR.
For post-release QA, at what point do you close the story? After all the subtasks are merged/deployed? Or later, after QA is done and they turn on the feature flag?
Our org traditionally estimates a product-oriented story with points, and then had one feature branch, which goes through QA and merge, at which point the story closes.
I think this causes all sorts of distorted effects, in that it simultaneously causes larger PRs, and also disincentivizes responsible coding practices that could grow the PR even more.
As these stories are product-focused, it's hard to break down the story into smaller stories, as this involves anticipating the tech work before starting, and makes it confusing to product folks if the stories start getting less product-focused.
I suspect it would be better to allow multiple PRs per story, or to otherwise push back on product? Should stories always be sized to accommodate one small PR?