Currently, there is no way to delineate the purpose of milestones in any sort of meaningful way other than the milestone name. As we're introducing the ability to assign issues to more than one concurrent timebox (#5135 (closed)) to support traditional planning methodologies such as Scrum, we need a way to identify what the milestone is from a data perspective so we can eventually add improved reporting across a given timebox type, auto schedule cadence for types, etc.
Auto moving issues from one milestone to another of the same type.
Issues to be associated to both a sprint, version, or release simultaneously.
Calculating velocity and volatility over n previous timeboxes.
Current users are tracking sprints within GitLab, then exporting issues on a regular cadence to track longer run timeboxes like releases or projects. This will alleviate the need to work externally from GitLab to get this level of reporting.
Proposal
Summary
Add the ability to enable/disable three timebox types within a top level group's settings.
Default is one enabled timebox named "Milestone". This cannot be toggled off. One timebox must always be enabled.
Fringe Cases
When an issue moves to another a project or group that does not have the same timebox types enabled:
Prompt with a warning dialogue - You are moving an issue into a group that does not have X, Y, and Z timebox enabled. These will be removed from the issue if you continue - Move AnywaysCancel
Stay inline with current milestones permission level of Developer and above.
Documentation
Yes, we will need documentation.
Testing
To be determined during the implementation planning.
What does success look like, and how can we measure that?
Success Metrics
Count of timebox types being enabled and which ones are used most frequently.
Acceptance Criteria
Proposal is generally satisfied.
GraphQL first
Rest API supported
gitlab-ui first. If new components are required, add them there first.
Telemetry added to capture success metrics.
Graph added to Plan dashboard in Periscope.
What is the type of buyer?
Based on our current understanding, any reasonable manager that is utilizing modern agile planning practices would buy this feature. Based on our four pricing tiers, this would fall into the GitLab Starter tier.
Links / references
None
This page may contain information related to upcoming products, features and functionality.
It is important to note that the information presented is for informational purposes only, so please do not rely on the information for purchasing or planning purposes.
Just like with all projects, the items mentioned on the page are subject to change or delay, and the development, release, and timing of any products, features, or functionality remain at the sole discretion of GitLab Inc.
Another thing we need to figure out: what if you change the type of a milestone? Say we have two milestones:
December is a Sprint.
12.6 is a Release.
And they are both assigned to this issue. What if we change 12.6 to be a Sprint? If we just let you do that it's going to put the issue into an invalid state.
Should we even allow changing milestone types initially? Or save that for when we can do it right?
I guess initially we can allow milestone type changes if milestone has no issues linked to it? I am thinking of the scenario when smbd starts setting up some milestones realizes one milestone can be better suited with a different type, forcing them to create a new milestone just to pick the right type seems unreasonable in that case.
As an extension on that thought I guess we check if for any of the issues for given milestone already have a milestone of new type to be assigned, and show an error message, or maybe possible to build a board/list with those issues to help user change the milestone(s) of that type on the issues.
I think I agree with @acroitor here: we should only allow changing a milestone's type if there are no issues linked to it. Otherwise it's a very expensive verification operation to make sure the rename will not place any issues in an invalid state (and we should avoid invalid states as much as possible!)
Can y'all elaborate on what an "invalid state" means to you? Also maybe elaborate on "it's a very expensive verification operation to make sure the rename will not place any issues in an invalid state"? @acroitor@mdelaossa
@uhlexsis for #5135 (closed) one of the requirements is that an issue must not be allowed to have two milestones of the same type.
If you change a milestone's type, there's a good chance that this constraint will be violated, so we have to check every single issue that has that milestone assigned to it before performing the change to validate that no issue will be in an invalid state (have two or more milestones of the same type assigned to it)
They can then attach that type in the issue under the type "bucket" (ie a user can attach a milestone defined as a sprint under the sprint area in the sidebar)
If the user edits the milestone type to something else, it becomes unattached from issues in which it was being used as the previous type (ie it would no longer appear under sprint in the sidebar because it is no longer defined as the sprint type)
@smcgivern it should be in GitLab Starter given the counterpart feature to associate an issue to multiple milestones is also in that tier. I've added the label to this issue. Thanks for the catch!
@mdelaossa could you please take a look and start planning out what backend work we'd need for this, along with a weight? As this is so closely related to #5135 (closed), you'll probably want to talk to @engwan to help define the boundaries between these two issues.
If you have any questions (I had one above in #35290 (comment 242150863)), please ask them here so Gabe and Alexis can take a look.
Gabe gives a bit more context on this feature (that's also in the issue, but video might be preferable to some people) in https://youtu.be/Q-6LNTSkE8M?t=116.
attributes: name (default to 'Milestone'?) plus Rails default attrs
has_many :milestones
belongs_to :group (no namespace, right?) We'll also want to validate the group it's being attached to is a top-level group (no parent_id)
We need to keep in mind projects that are part of a namespace and therefore do not have a top-level group! Any code written with milestone types in mind should treat a nil type as a default milestone. Furthermore, initially a lot of milestones will have a nil type unless we decide to mass create and update, which would be extremely expensive.
Callbacks
cascade deletion for milestones, definitely
upon creation of the first 'type' for a group, a background worker should launch to update all existing milestones to that type
Validations
Needs to belong to a top-level group
2. Milestone
belongs_to :milestone_type (a Milestone can only be one type).
Callbacks
We could add a callback on creation here that makes sure a MilestoneType exists for the top-level group. Otherwise, the validation below will have to special-case when no MilestoneType exists for the group.
Validations
milestone_type_id must be present unless the project is not part of a group (namespace owner). This does mean existing milestones would be invalid, so this validation should only run on creation for now
3. Group
has_many :milestone_types
Callbacks
after_commit, on: :create so we create a default MilestoneType per top-level group
on the DB level, we'll want a cascade deletion for milestone types
Validations
Only up to 5 MilestoneTypes may exist per group
A minimum of one MilestoneType per group must exist
Controllers
Not many thoughts here - from a backend perspective having a basic MilestoneTypesController with CRUD operations (albeit being protective of the deletion operation as it would cascade and possibly affect a large amount of issuables irrecoverably). The only special consideration I see is that when a user first visits this controller we would create the first "default" type and update all existing milestones in a background worker to be this new type
We also want to update both Groups::MilestonesController and Projects::MilestonesController to be able to add a milestone type.
Workers
We need a background worker to update all existing milestones in a group/project hierarchy when the initial MilestoneType for a particular top-level group gets created
Other thoughts
We have GroupMilestone which is mostly used for legacy milestone display, so it seems safe to ignore
To reiterate: milestones belonging to projects inside namespaces will always need special handling as their "milestone_type_id" will always be nil
Initially, existing milestones will have nil types, so we should make sure our code understands this and we create an initial MilestoneType as soon as it makes sense, but since the operation can be very expensive we don't want to do it prematurely
With the above in place, whoever works in #5135 (closed) should be able to just make sure the milestone_types differ when assigning them to an issue (this is why I'm advocating for updating all existing milestones in the background when
Since we're restricting to top-level namespace, I think we'd have to handle moving of namespaces.
As a follow-up, maybe we should allow quick creation of a default set of milestone types just like we do with labels
So that we don't need to do mass-updates, would it make sense to have nil as the default type? I think this is what is meant by Default to 1 type with a sensible default name like Milestone. in the description.
So for example, an issue could have one "Release" milestone, one "Sprint" milestone, and one "Milestone" milestone.
If multiple milestones gets done / released first, we would end up in a state with issues having multiple milestones of the same type (the default type).
We would have to think about changing milestone types. Should we allow this?
If we do, should we prevent a change if it results in issues having more than one of the type? Or do we want to automatically remove milestones with the same type after it is changed.
Hmm, that might be unexpected. @uhlexsis do you think deleting a type should delete all milestones of that type?
Maybe we should also have a bulk update UI, but that depends if we allow changing types or not.
Because of the wrinkles related to updating existing milestones I'm very tempted to mark this as a 5, but it could end up being a 3
Let's go with 5. It sounds like we might want to break some of this up, too.
Since we're restricting to top-level namespace, I think we'd have to handle moving of namespaces.
Good point! If a namespace moves from a group without milestone types to one with milestone types, all milestones can just have the default type. But moving between two groups with different milestone types is tricky
We do forbid moving namespaces in some circumstances but I think that is quite unfriendly.
We would have to think about changing milestone types. Should we allow this?
@mdelaossa do you think it's worth moving open questions for Product Management and Product Designers to individual comments? (Cascading deletes, namespace moves, changing types, etc.)
@smcgivern thanks, I created separate comments for the open questions. I didn't create one for changing types since you already have a separate comment asking that
If multiple milestones gets done / released first, we would end up in a state with issues having multiple milestones of the same type (the default type).
good point. We'll probably want to feature flag this until multiple types is deployed, otherwise I see a very messy transition
Hmm, that might be unexpected. @uhlexsis do you think deleting a type should delete all milestones of that type?
I am thinking we warn users that deleting a milestone type will close associated milestones, then a user could reopen a milestone if that is not what they intended. I am not sure how this would affect their charts and tracking though?
We could also perhaps have any milestone not specifically tied to a type default to milestone, so in that case- if a user deletes a type, all the milestones associated with that type would then become milestone type. I haven't thought too far into that flow though.
If we do this, it needs to be a VERY difficult action for a user to take. i.e. double confirmation, explaining how many milestones would be deleted, the number of issues impacted, and that all relevant data will be permanently lost. At the end of the day, we need to provide folks with the ability to control their own destiny, so it feels like we will need to support this.
In a follow on iteration, I would like to expose the ability to hide certain milestone types within sub-groups because there are instances where sub-groups may not use all of the "global" fields. This is a current pain point folks have with JIRA and is something we should plan on addressing in the near future.
We could potentially launch the first iteration without the ability to delete, but it would need to come quickly after the first MVC.
Q: How can we support moving projects between groups?
Sean commented:
Good point! If a namespace moves from a group without milestone types to one with milestone types, all milestones can just have the default type. But moving between two groups with different milestone types is tricky
@mdelaossa@uhlexsis I like the suggestion and recommend we explore that. Maybe we could also extend this to include label mapping as it suffers from the same root problem.
As an interim step, we could show a warning dialogue that communicates something along the lines of:
You are moving an issue into a group that does not have X, Y, and Z milestone types. These will be removed from the issue if you continue - Move AnywaysCancel
Q (backend): Do we want to create a default MilestoneType per top-level group and mass-update all existing milestones with a default MilestoneType, or treat nil as a default type with name 'Milestone'?
I don't see any cons to special-casing nil, tbh. I think the expense of creating a MilestoneType object for every single top-level group out there right away is not worth it, even if we didn't automatically update every milestone to have that milestone_type_id
@mdelaossa I was assuming we would do the mass-update once, but I don't have a strong opinion. From a UX standpoint, I want to try and minimize the disruption of going from one milestone with a fixed name of milestone to issues being able to have up to 5 milestones that could be named literally anything.
If we don't create a default milestone type for top-level groups, what would we show on an issue?
Should we allow users to create new from here, or just have the one default item of milestone in the list? It makes settings more discoverable, but would we be able to save their place for them when they come back to the milestone (like issues)? This is a tricky flow that we can keep iterating on as we make things more real time.
2. Milestone defaults for Milestone types in settings
The placement of this in the settings list could be iterated on
Thoughtful descriptions and help text can guide user understanding around what these are and perhaps what limitations exist
3. Creating a new milestone type
This is a similar pattern to the "Add/create issues/epics in the issue widget" flow and has similar possible problems as well. Some usability testing here would be helpful to determine what a user would expect to happen if they clicked out of the "New type" box.
4. New milestone type has been created an appears in the list
5. Many milestone types have been created
The new button is disabled once the limit has been reached
This is pretty rough, design on the milestone type indicator/badges can be iterated on or improved here
6. Editing a milestone type
This is a similar pattern to the "Add/create issues/epics in the issue widget" flow and has similar possible problems as well. Some usability testing here would be helpful to determine what a user would expect to happen if they clicked out of the "edit" box.
7. Milestone type limit has been reached
Tooltip copy would have to be thoughtful here.
8. Reordering the list which will reflect on the issue page
9. Deleting a milestone type
10. Selecting one of the new milestone types in the dropdown when creating a new milestone
Would "Milestone" still be default here?
Would this list reflect the ordering the users chose in the settings or something else?
11. Creating a new issues with ordered milestone types appearing in the sidebar
Improve the design of this when we tackle the sidebar.
The types could eventually all live under one header, something like "Timeboxes".
12. Assigning a milestone to each milestone type
Similar to current experience
Only milestones defined as the type appear within each dropdown (ie sprint)
13. Each milestone has/can have one type attached
14. Milestones in the milestone list display what type they are
More design work could be done here to better define what this looks like. We could do this now or when we redesign the milestone page.
15. Editing a milestone type in the milestone page
Should perhaps have some alert saying "hey this will unattach this from issues"
16. Edited milestone no longer appears under the sprint milestone type
17. Number of milestones is reflected in the issue list
18. Number of milestones is reflected in issue widget
What should happen to the milestone type of the milestones being closed, is my main problem. Do we default it to the first type in the list? Leaving it nil should mean the same as defaulting it to first type in my opinion, as we're planning on doing that defaulting in other places
10. Selecting one of the new milestone types in the dropdown when creating a new milestone
Would "Milestone" still be default here?
I think we should default to the first in the list of milestone types. After all, why are we letting the use reorder them otherwise?
Would this list reflect the ordering the users chose in the settings or something else?
Same as above, I think this has to be the user-selected order as otherwise I see no reason to allow the user to reorder them at all
11. Creating a new issues with ordered milestone types appearing in the sidebar
I feel like this makes the feature look dangerously close to another much-wanted feature: custom fields for issues. See: &235 and gitlab-foss#41865 (closed)
To be fair, looking at that screenshot, there really isn't anything functionality-wise stopping someone from using it as a stop-gap custom fields feature (except for the 5-type limit of course) - but if we later add custom fields it could get confusing
17 & 18. Number of milestones is reflected [...]
Good idea, we should default to showing the milestone at the top of the user-ordered list though
I'm still concerned about allowing a user to delete types, and change a milestone's type - I'd be much more comfortable with an edit-only flow for types (after all only the name changes! ... though I do understand maybe extra rules in the future mean that deleting is better) and I don't really see a good compromise for milestones. We'll just have to take the hit
What should happen to the milestone type of the milestones being closed, is my main problem. Do we default it to the first type in the list? Leaving it nil should mean the same as defaulting it to first type in my opinion, as we're planning on doing that defaulting in other places
I am not I understand what you are asking here. What I think you are wondering is what happens to the milestones that are attached to a milestone type that is being deleted? I was envisioning the milestones would close (check out the modal in the design). That would lead to some kind of strange "milestone type-deprecated" kind of state if the user looked at the closed milestone though. If they tried to reopen the milestone, that "deprecated" milestone type would not exist, so we would either have to prompt them to select a new one or choose a default for them. I think I'd actually rather we let them choose rather than choose for them, so if we go this route we'd have to take a look at that "reopening" flow.
I feel like this makes the feature look dangerously close to another much-wanted feature: custom fields for issues. See: &235 and gitlab-foss#41865 (closed)
Yep, I saw this too. We could just give users some defaults to choose from, but this is less flexible and one aspect of this issue was allowing users to set their own so that we could get some data around what defaults they would want and why. This is something @gweaver could speak to and something we could ideate with him on.
I'm still concerned about allowing a user to delete types, and change a milestone's type - I'd be much more comfortable with an edit-only flow for types (after all only the name changes! ... though I do understand maybe extra rules in the future mean that deleting is better) and I don't really see a good compromise for milestones. We'll just have to take the hit
I hear ya. This is something we should have a conversation about/collaborate on! Thanks for all your great insight here!
Since I am OOO right now, @annabeldunstone and @hollyreynolds could y'all keep track of this issue and perhaps give it some thought as well? I know this is a priority for Gabe.
@hollyreynolds@gweaver I'm putting my feedback here (not mentioning Alexis as she's out). These are the only parts I had comments on, and I'm avoiding commenting on things @mdelaossa mentioned - the rest looks good to me.
The screenshots all show this in a project settings context, when it should be in a group settings context. This isn't material - all of the changes should still work on a group settings page - but I wanted to call it out.
I don't think we need custom ordering for an MVC.
I don't think we need the '+2 more' on the issue list. We already show all labels, with no limit, on the issue list. An issue can only have five milestones anyway so hiding some of them seems unnecessary for an MVC.
Moving on from the last comment I pinged ya on in this issue @hollyreynolds - I was playing around with the deletion flow and would love your feedback or ideas! These are all pretty rough, just trying to ideate on the flow. I am assigning you to this issue with me for now while I am OOO and at capacity so that you can take a look and potentially flesh it out or move it forward as well.
One
There is no longer a delete option. This flow could be similar to the closing of epics or milestone that contain issues (the epic or milestone closes but is still visible and does not close work contained within). I am assuming an "archive or close of some sort instead of a delete makes sense and provides more traceability. I am also playing around with the idea of allowing the user to set a default milestone type. This default will apply when creating new milestones. I like the idea of establishing a default as the reordering I mentioned in my last comment implies more of a workflow or funnel, and not really a sense of priority.
Two
Limit reached
Under limit
Once a milestone type is archived, it appears in the archived types area. If the milestone type limit has not been reached, a user can restore an archived type. If the limit has been reached they can't restore an archived type until they archive another.
This could be styled as tabs which is pretty common in our UI, but I liked having both states exposed so a user can better understand their limits and the status of milestone types as they potentially are being sorted and may change status from archived to not multiples times.
Three
Milestone list indicator
Edit milestone dropdown
After a user has archived a milestone type (ie sprint), it doesn't disappear and milestones are still attached to it and open. It instead goes into some type of deprecated/archived state. This is similar to how open issues can still belong to a closed epic. Users can view it in a historical view type of way, but the type "bucket" no longer appears in the sidebar for users to interactive with (ie a user wouldn't be able to define what their sprint is in an issue because they have archived that milestone type).
In the future I think it would make sense to allow users to sort/filter by milestone type, as well as view what milestones and issues are attached from the milestone type view itself. Then we could perhaps allow users to move or bulk update milestones and issues attached to any given type.
@uhlexsis I love the idea of users being able to sort & filter by type! The bulk move/update feature is something I think could be really useful when managing large numbers of issues under a single type, particularly if there is a need to delete or rename the type and shuffle work as a result to a different type.
The concept of no longer having a delete is interesting. What happens when an issue is deleted or edited I think is my biggest concern at this point as I think the rest of your flows look good! What happens with an issue if it's tied to an archived type? How does that impact reporting? Is there anything else that gets changed? @gweaver this may be something you can help with while Alexis is out.
@uhlexsis@hollyreynolds We will still need to remove the archived milestone type from all associated issues and should give a strong warning of what will happen. I don't mind archiving, but the end impact to the historical data on the milestone and the issue itself will still need to be basically the same as if the milestone type were deleted -- so what's the inherent value?
I feel strongly against having yet another sorting and filtering view. I would prefer we pre-sort milestones by type via a simple linked list dynamically built from the types that are currently active, use tabs, or something similar at the top of the milestone view. I'll also still advocate that milestone type configuration should live within the milestone view via something like a milestone settings page ;)
@uhlexsis here is a list of my initial feedbacks. A lot of it is carryover from the concepting call.
Screen 1: This would be fine but only at the top level group. We could also consider adding an “edit” link in the drop down that would open a new tab direct to the settings.
Screen 2: I feel pretty strongly about putting this on the Milestone page (even a simple button somewhere that transitions between the config stuff and the actual milestones). Also think once we have the types, we could use tabs or a linked list to easily jump between the list of each type of milestone. Happy to be proven wrong about where we initially place the type settings ;)
Screen 3: Since the types living with the collapsable Milestones area, if we end up leaving the settings here, shorten “Milestone Types” to “Types” — I don’t like the word types either so if anyone has a better suggestion, I’m all ears.
Screen 4: Visually, is it necessary to use so many boxes? I tend to prefer less design and only use containers when absolutely necessary. We copied eliminate the different edit view all together by just making the row fields editable when you click the pencil icon. In edit mode, the pencil icon switches to a checkmark or some other “save” icon in an action color.
Screen 8: Big fan of re-ordering. We can break this out into a follow on issue if it adds too much complexity.
Screen 9: This works. Will want to indicate how many issues will be impacted as well as clearly indicating this is permanent data loss.
Screen 11: Don’t be shy about improving the sidebar design as part of this process. Too many edit buttons.
Screen 14: Consider using a linked list / tabs at the top of the page to group these by type instead of introducing yet another filter. We should at least start to think about how we want to improve the milestone page as part of this process. Even if we wait a release or two to do the work, understanding the ideal state were driving towards could be useful (even if done via lo-fi wires)
Screen 15: Yes, a strong warning should be present with this action as it will break links between issues and milestone.
@uhlexsis I've broken out deleting (#118731 (closed)) and reordering (#118738 (closed)). What else do we need to wrap up on this from a design perspective in order to get the MVC ready for development?