DEV Community

Cleo Buenaventura
Cleo Buenaventura

Posted on

Refactoring GENEREADME

For this week's lab, I was tasked to refactor my code, genereadme with the opportunity to learn rebasing, squashing, and amending commits. I am tasked to refactor at least 3 things in my code, which in a normal case shouldn't be an issue. However, before someone asked to contribute to my project last week for last week's lab, I did a major refactoring since all the code was only in a single index.js file. The task for last week was to add another feature, which means the project is going to get even bigger. So with that in mind, I decided to refactor it.
Because of this, I struggled a little bit to find more things to refactor in my code as I have refactored it to as much as I think I can, leaving me a bit surprised as I did not expect refactoring to be a part of the labs.

What to look for

Well, there are many ways to refactor code. It could be renaming variables, extracting functions, creating classes, splitting code, or optimization. My code isn't really that complex, I am confident that the variable names were self explanatory. So I focused in trying to look for any remaining code duplication or shared functions that I could refactor.

Refactoring

I was able to find 2 files to refactor, which I extracted more functions and split them into separate files. Since I just did a major refactoring a week ago, my goal here is to improve readability and maintainability.

First, I had a file setup.js which contains the initialization of Commander that also includes adding the basic settings, added options, added arguments, and any custom error handling. By looking at it, this file will end up getting bigger as more possible option flags can be added, possibly more custom error handling for new features, or accept an entirely new argument for a different feature.

  const program = new Command();

  program
    .name(packageJson.name)
    .usage("<filename> [options]")
    .description(
      "CLI tool to generate a README file explaining a source code file"
    )
    .helpCommand(false)
    .addHelpText("after", "\nSupported providers: [Groq, OpenRouter]")
    .version(
      `${packageJson.name} ${packageJson.version}`,
      "-v, --version",
      "Outputs the tool name and current version"
    )    .option("-p, --provider <provider>", "Provider for the chat completions")
    .option("-o, --output <filename>", "output result to specified filename")
    .option("-a, --api-key <key>", "API key for the Groq API")
    .option(
      "-t, --temperature <temperature>",
      "Temperature for the chat completions"
    )
    .option("-tu, --token-usage", "Show prompt and completion token usage")
    .configureOutput({
      outputError: (str) => {
        if (str.includes("error: missing required argument")) {
          console.error(
            "Error: No source code file provided. Please provide a valid source code file to process.\n\nuse command: genereadme <files...>"
          );
          process.exit(1);
        } else {
          console.error(err);
          process.exit(1);
        }
      },
    })
    .argument("<file...>", "Source code file to process");

  return program;
Enter fullscreen mode Exit fullscreen mode

So what I did, is separate them into different functions categorically. Mainly, I have a createProgram() function which contains the construction of the Command object and the basic settings, such as program name, version command, help command, and etc.
Then, I created the other functions by category to organize them, making the code readable and maintainable. For example, adding an additional option would require adding .option() to the Command object. Having them all in one function makes it readable as these methods are grouped into one function, which I then did the same to the remaining program settings.

Second, a simple change I made is I basically extracted a logic into a function.

  let baseURL;
  let model;

  if (provider.toLowerCase() === "groq") {
    baseURL = "https://api.groq.com/openai/v1";
    model = "llama-3.1-70b-versatile";
  } else if (provider.toLowerCase() === "openrouter") {
    baseURL = "https://openrouter.ai/api/v1";
    model = "meta-llama/llama-3.1-8b-instruct:free";
  } else {
    throw new Error(
      `${provider} is invalid or is currently unsupported. Please choose from the supported providers:\n` +
        "1. Groq\n" +
        "2. OpenRouter"
    );
  }
Enter fullscreen mode Exit fullscreen mode

This is the logic I have which allows users to use different LLM providers, which I had included in the same file where I am constructing the OpenAI client object.

Merging

So far, through the use of git, source control, or github desktop, I would normally just make my commits, push the commits to my working branch, then either make a merge or PR. Now I know how to rebase and squash multiple commits into one, and using amend whenever I need to fix something from the last commit.

Top comments (0)