In order to provide the changes in an iterative and incremental manner, complex changes should be split into smaller ones to simplify the review process. As a result, more people are involved in the particular feature development as a whole that helps to receive diverse feedback. Meanwhile, fewer people are involved in one particular merge request that makes the collaboration more effective and scoped to a particular piece of functionality.
Responsibility sections are usually the hints to identify the split. A merge request which contains ~frontend, ~backend,
~database labels requires at least 6 people to review it from 3 different perspectives. In this case, the review process is not only slowed down by back and forth, but also contains the discussions which are useful for one section, but not for another. A huge drawback of combining these sections, is that 2 of those portions could be approved, but the whole thing slips because there are changes required on 1 of the sections. A merge request should contain non-trivial changes from only one section to ask for a quick review for the auxiliary section and continue with the detailed review for the main one. It’s not possible, for example, to completely separate
~database and ~backend changes, but we need to make sure that in this case ~backend changes are as simple as possible and doesn’t distract from
A functionality behind a feature flag is a great candidate for being split into multiple merge requests because in this case even an imperfect piece of functionality can be introduced without breaking the existing one. For example, if ~frontend work is behind a feature flag, it can be merged separately without waiting for backend changes; otherwise, it can be still provided in a separate merge request on top of the WIP ~backend changes and merged just after those API changes are introduced.
Consider extracting edge-cases that aren't an impediment to introducing the smallest working piece of functionality into follow-ups. That will save the merge request from growth during a review. The follow-ups are also useful for distributing the work among the team when the issue turned out to be more complex than expected.
Refactorings that require a complex approach can be introduced separately either before the actual changes or as a follow-up in case the quality doesn’t suffer from the introduced changes.
If your issue/merge request description or comment says "First we should do this, make sure that works, refine this", it is also a sign of a possible split that happens before work even begins. In general, if an engineer is following the "one commit, one logical change" model, then each commit can potentially be a separate merge request.
Introducing the changes that don't interact with the existing codebase, make sure that you have a clear plan of how that code will be used in the follow up issues in order to avoid creating blocks of unused code. Consider explaining the reason in comments or commit messages.
Consider marking blocking merge requests as merge request dependencies
When a change involves separate ~backend and ~frontend merge requests, include unit and integration tests in the merge request with the code they're testing. Include feature specs in the ~frontend merge request so that when the ~backend changes are merged, the feature specs can run against the combined changes before they're merged and exposed to users. On the other hand, end-to-end (QA) tests can be introduced separately.