1. From merging to proposing: why do we need Pull Requests?
In the previous lecture we learned how to merge branches on our local machine. That works great when you're working alone. But what if a whole team is working on the project? If everyone merges their changes directly into the main branch main, chaos will start soon: someone can accidentally add buggy code, break the build, or delete an important piece of a colleague's work.
To avoid that, teams use a different approach. Instead of merging changes right away, you create a merge proposal. This proposal is called a Pull Request (PR) or, on some platforms, a Merge Request.
Pull Request is an official request: "Please pull my changes from my branch and merge them into the main branch". But it's not just a request — it's a whole place for discussion, review, and improving code before it lands in main.
2. Typical workflow with a Pull Request
Let's go step by step through the typical workflow when you create a new feature.
Before creating a branch for a new task, make sure you've pushed all your finished commits (push) and updated (Update Project) the main branch main.
Otherwise all your "forgotten" local commits from main might accidentally end up in the new Pull Request and confuse your teammates.
Step 1. Create a branch for the new task.
As before, any new work starts by creating a separate branch. Suppose we want to add a contributor guide file to our project. Create a branch feature/add-contribution-guide.
Step 2. Make changes and push your branch to GitHub.
In the new branch create the file CONTRIBUTING.md (after creating press Add) and write a message to other developers. Then make a Commit and Push with a clear message, for example: docs: Add contribution guide.
This is a key step! A Pull Request is created from a branch that already exists on the remote. So before creating a PR, you need to push your new branch to GitHub.
3. Creating a Pull Request from IntelliJ IDEA
Now that your branch is on GitHub, you can create a Pull Request.
Step 1. Open the Pull Requests tab.
On the left in the IDE there's a Pull Requests tab. Open it and click the + icon to create a new PR.
Step 2. Fill in the PR details.
The IDE will open a nice interface for creating the Pull Request. Your job is to fill it out properly. Let's look at the main fields:
- Title: the IDE often fills this with the branch name, but that's bad practice. The title should be short, clear, and reflect the essence of the changes, like a good commit message.
- Description: here you explain what and why you did.
- Reviewers: here you pick one or more colleagues who should review your code. We'll skip this in the learning project, but in real work it's mandatory.
- Assignees: usually you put yourself here. That means you're the author and primarily responsible for the task and for applying fixes after review.
Once all fields are filled, confidently click Create Pull Request.
4. Code review: checking and discussing
After creating the PR the most important phase starts — code review (code review). Your teammates can open your PR, see all the changes and leave comments.
You can see all discussions right in the IDE under the Pull Requests tab. If someone leaves a comment you'll get a notification.
What to do if you're asked to make changes?
Super simple! You don't need to create a new PR. Just make the needed changes in the same branch, commit again and push. The Pull Request on GitHub will update automatically with your new commits.
5. Finishing up: merging and deleting the branch
When all comments are resolved and the team approves your changes, you can merge the Pull Request. Usually this is done by a senior dev or by you if you have permissions.
Step 1. Merge
Merging most often happens on GitHub. There will be a big green button Merge pull request under your PR. After clicking it your code becomes part of the main branch main.
Step 2. Delete the branch.
After merging your feature branch is no longer needed and should be deleted to avoid cluttering the repo. GitHub will offer to do this by showing a Delete branch button.
Don't forget to delete the local copy of the branch in your IDE too, to keep things tidy. You can do that from the branch management menu.
6. Three commit rules
A good commit is not only a clear message but also the right content. To keep your history clean, useful, and professional follow three simple rules.
Rule 1: write clear messages using a standard
Your commits are messages you send to your team and to your future self. A history full of "fix" or "update" messages is useless. The most popular standard is Conventional Commits. It suggests this structure:
<type>: <short description>
Type is a short word describing the category of your change:
feat: (feature) — for new functionality.fix: — for bug fixes.docs: — for documentation changes.style: — for formatting fixes that don't affect code logic.refactor: — for code changes that don't add new functionality or fix bugs.test: — for adding or fixing tests.chore: — for routine tasks not related to application code (dependency updates, build setup).
Examples:
- Bad:
fixed bug - Good:
fix: Correct user login validation
- Bad:
readme - Good:
docs: Update installation instructions
Rule 2: one commit — one logical change (Atomicity)
Don't try to shove a bug fix, a new feature and a refactor into a single commit. Such a commit is hard to review and almost impossible to revert safely if something goes wrong.
Each commit should solve exactly one task.
- Bad: one commit with message "Update user page" that adds avatar field, fixes username validation bug, and changes button colors.
- Good: three separate commits:
feat: Add avatar upload to user profilefix: Correct username validation logicstyle: Update button colors on user page
Small, focused commits are much easier to understand and manage.
Rule 3: A commit must not break the project
Every commit on the main branch should leave the project in a working state. Before creating it you should at least make sure the code compiles. But how to be sure you didn't accidentally break something else? Relying only on manual checks is risky.
This is where automation helps. We won't set up automated pipelines in this course, but you should know how it works in real projects. Modern teams use Continuous Integration (CI) systems like GitHub Actions.
How does it work?
You write code and tests for it. Then you configure a special workflow on GitHub. Now, as soon as you push to your Pull Request, the magic happens:
- GitHub Actions notices the event and runs your workflow.
- It automatically builds the project and runs all tests.
- If all tests pass, a green check appears next to your commit on GitHub. That's a signal to the whole team that your changes are safe.
- If at least one test fails, you'll see a red cross. Merging such a PR into the main branch is strictly forbidden.
graph LR
subgraph GitHub
A[Developer] -- "push" --> B(Repository)
B -- "Event: push" --> C{GitHub Actions}
end
subgraph Workflow
C -- "Run" --> D[Run_tests]
D --> E[Success]
D --> F[Failure]
end
subgraph Notifications
F --> G((Email))
G --> H[Developer]
G --> I[Team]
end
style F fill:#f99,stroke:#333,stroke-width:2px
style G fill:#ccf,stroke:#333,stroke-width:2px
You can configure notifications. If tests fail, GitHub Actions can email you or the whole team. This approach builds a culture where tests are not just a formality but an integral part of development, and everyone is responsible for the quality of their code.
GO TO FULL VERSION