DEV Community

Leonardo Holanda
Leonardo Holanda

Posted on • Edited on

The VS Code Extension That Helps You Control Your Pull Requests Size

When I was working as an intern developer in a startup in my hometown, I noticed one particular thing that was quite annoying: large pull requests.

In this post, I'm gonna talk more about Changes Counter, a VS Code extension I made to tackle this problem. Here's what you will see:

The Problem

If you have reviewed a PR with thousands of lines, you know that it ain't fun. It takes time and the more time you spend on it, the more tired you get while also becoming harder to find bugs and mistakes in the code.


This tweet is a classic when posts are made about pull requests

In the context of my internship, I remember seeing some large PRs taking so long to be merged that it would affect the progress of other tasks resulting in bottlenecks.

And there's actually a study on this made by a company called SmartBear which recommends reviewing "no more than 200 to 400 lines of code at a time".

In summary: large pull requests suck.

Why would they happen in my team?

During the Scrum ceremonies, our team would decide which histories and tasks to create on the Jira board. Most of the time, we would agree that the tasks' scope was fine and reasonable. However, when working on these tasks, we noticed that they required more changes than predicted.

Sometimes, we would push the changes to the remote branch and create pull requests with lots of changes. Sometimes, we would notice that it would be too much for our reviewer friend and split the task into two separate ones.

But this whole process would rely on intuition rather than the actual number of changes that we would make in the PR. And being a team of interns, this intuition hasn't yet got enough time to develop itself and achieve good results. The same thing applies to task creation, I think.

So I thought: What if the dev would know exactly how many changes will go in the PR while coding the task instead of relying on intuition?

This way, the dev will always know if the PR is getting larger than desired and then decide to take some action.

The Solution

One of the extensions that we were encouraged to use in the internship was GitLens. It provides lots of features to make the whole git experience better and it actually would come in handy frequently during the work we did.

While using it, I noticed that the Search & Compare feature contains data about changed lines of code. But unfortunately, not in a way that could solve our problem.


GitLens Search & Compare feature screenshot

To solve our problem, the data needed to be presented in an easier way to look at while being more useful to the developer during the process of working on a task. Kinda like a status bar item like the Errors & Warnings.

Also, a notification warning the developer that a given change quantity threshold was exceeded could be nice in case the dev was unaware of it.

I ended up with these requirements:

  • A status bar item that shows how many lines of code were changed
  • The item is updated every time a file is saved
  • The user can set a threshold to determine an acceptable quantity of changed lines
  • A notification is sent when the threshold is exceeded

The Implementation

With the requirements in mind, I decided to develop a VS Code extension myself and try to tackle these requirements into features.

I knew nothing about coding VS Code extensions. However, this documentation was really valuable in giving the base extension code and instructions on running it in a dev environment.

Then, it was a matter of adding the features I wanted. Lots of things were actually simple and I don't think it's worth mentioning here. But this one problem was interesting to me:

How to run Git commands in TypeScript?

The first question I had was: "How to count the number of lines changed?". Since I already knew about git diff, it was just a matter of running this command and working on its output.

But since VS Code extensions are developed with TypeScript in a Node.js environment, how would it be possible to run git diff and hold its output inside the extension code?

Searching about it, I discovered the Child Process module from Node.js. You can use it to spawn a subprocess that can run git commands. This is the code provided in the documentation as an example:

const { spawn } = require('node:child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
}); 
Enter fullscreen mode Exit fullscreen mode

Bringing this to the extension code, we get something like this:

  async getDiffData(): Promise<DiffData> {
    return new Promise((resolve, reject) => {
      const comparisonBranch = this.context.workspaceState.get<string>("comparisonBranch");
      if (comparisonBranch === undefined) {
        reject("A comparison branch wasn't defined. Please, define a comparison branch.");
        return;
      }

      const gitChildProcess = spawn(
        "git",
        ["diff", comparisonBranch, "--shortstat", ...this.diffExclusionParameters],
        {
          cwd: vscode.workspace.workspaceFolders![0].uri.fsPath,
          shell: true, // Diff exclusion parameters doesn't work without this
        }
      );

      gitChildProcess.on("error", (err) => reject(err));

      let chunks: Buffer[] = [];
      gitChildProcess.stdout.on("data", (chunk: Buffer) => {
        chunks.push(chunk);
      });

      gitChildProcess.stderr.on("data", (data: Buffer) => {
        reject(data.toString());
      });

      gitChildProcess.on("close", () => {
        const processOutput = Buffer.concat(chunks);
        resolve(this.extractDiffData(processOutput));
      });
    });
  }
Enter fullscreen mode Exit fullscreen mode

By calling this function, we get all we need through the resolved promise: an object containing the changes data or the error we must handle.

Logging

A thing I learned while developing this extension is how important logging is. When some users reported problems, the lack of information was something that made debugging difficult.

When I started logging some lifecycle events and errors that could eventually appear, I noticed that it would be far easier to debug once the user sent me the log. Knowing where to search for the bug is obviously crucial.

If you are developing VS Code extensions, try to introduce logging as early as possible to avoid debugging in the dark.

And don't forget to search for the best practices before doing it. They are simple and can be helpful to make your logging more consistent with other applications. Here's an example.

The Result

You can see the extension page at the Visual Studio Marketplace here.


Changes Counter extension

And you can see the code here.

Any feedback and suggestions are more than welcome!

What I learned from all this

  • How to create and deploy a VS Code extension
  • How to spawn subprocesses and run system commands with the Child Process module from Node.js
  • The importance of logging

Top comments (4)

Collapse
 
icolomina profile image
Nacho Colomina Torregrosa • Edited

Fantastic Leonardo !! Many thanks for such a great extension !

Collapse
 
leoholanda profile image
Leonardo Holanda

Thanks Nacho!! I'm glad you liked it!

Collapse
 
wraith profile image
Jake Lundberg

Such a great idea! We’ve recently implemented a new policy that requires PRs to be fewer than N changes. something like this could be really helpful for our team! Thanks for sharing!

Collapse
 
leoholanda profile image
Leonardo Holanda

That's great, Jake! I'm glad you liked it and I hope that it can be useful for your team! Any bugs or suggestions, please let me know!