This week in my Open Source Development class, my classmates and I were given a task to think of three ways to refactor
the code we wrote for our command-line
tools.
Below, is my GitHub repository for my command-line tool gimme_readme
:
peterdanwan / gimme_readme
gimme_readme is a command-line tool powered by AI that generates a comprehensive README.md file for your project. It analyzes multiple source code files at once, providing concise explanations of each file's purpose, functionality, and key components, all in a single, easy-to-read document.
gimme_readme
gimme_readme
is a command-line tool powered by AI that generates a comprehensive README.md
file for your project. It analyzes multiple source code files at once, providing concise explanations of each file's purpose, functionality, and key components, all in a single, easy-to-read document.
See our 0.1 Release Demo!
Table of Contents
- Getting Started
- Usage
- Example Usage
- Supported Models by Providers
- Contributing
- Testing Locally
- Author
1. Getting Started
To get started with gimme_readme
, follow these steps:
-
Install the latest version of Node.js for your operating system.
-
Run the following command to install
gimme_readme
globally:npm i -g gimme_readme
NOTE: MAC/LINUX users may need to run
sudo npm i -g gimme_readme
-
Generate a configuration file by running in any folder you'd like:
gr-ai -c
This command creates a
.gimme_readme_config
file in your home directory. Do not move this file from this location. -
Open the
.gimme_readme_config
file and add your API…
Before we go into what I refactored, I think it's best I clarify what refactoring is. Refactoring
is the process of improving the internal structure of your code without changing its current functionality. In comparison, revising
has the connotation of making changes that could change the current behaviour of your program (hopefully for the better).
With definitions out of the way, let's get into what I refactored.
To me, the most obvious thing I could do to refactor my code was to facilitate better file organization within my repo.
I want to organize my repository in a way that tells the story of the project. By placing files in logically named folders, anyone should be able to navigate it easily and know where each file belongs.
This is what my repo's file structure looked like prior to my changes:
This is what my repo's file structure looks like now:
The hope with this refactoring of my project's file structure, is that contributors, as well as myself can quickly reason, "if I want to work on the ai-related
logic of this program, I should probably look in my ai
folder".
But wow - this was a lot of work to implement.
By attempting to move my ai_config
folder and ai_models
folder into the ai
folder, I also had to update the plethora of other files that depended on the files within these folders.
Take a look at how many files I needed to change, simply because I wanted to rename my folders:
Look at this end result:
The end result, in my opinion, is a lot nicer - the right files are where they should be now.
As a side note, you'll notice that when I ran:
git status -s
You could see files that had the R
beside them - this is to indicate these files were renamed and that git
is aware of this.
git
is aware of this renaming because I had executed git mv
commands such as this:
git mv tests/unit/ai_models/groqModels.test.js tests/unit/ai/models/groqModels.test.js
Now - why should you care?
Well, if you were in a situation where you wanted to rename your existing files in a git repository, and you didn't use git mv
to rename your files, you might encounter several issues:
-
Loss of file history
: If you manually rename a file (e.g., using your operating system's file explorer or a command like mv), git will interpret this as two separate actions: deleting the old file and creating a new one. This means you'll lose the file's history in git, making it harder to track changes over time. -
Confusion in diffs
: When you review changes or create pull requests, the diff will show a file deletion and a new file creation instead of a simple rename. This can make code reviews more challenging and time-consuming. -
Merge conflicts
: Renaming files without git mv can lead to more merge conflicts, especially if other team members are working on the same files. -
Incomplete tracking
: git won't automatically track the relationship between the old and new filenames, which can complicate operations like reverting changes or cherry-picking commits.
By using git mv, you ensure that:
-
File history is preserved
: git maintains the file's entire history, just under a new name. -
Clean diffs
: When you review changes, it's clear that a file was renamed rather than deleted and recreated. -
Smoother merges
: git can handle merges more intelligently when it knows a file was renamed. -
Proper tracking
: git correctly tracks the relationship between the old and new filenames, making operations like reverting changes easier. -
Efficiency
:git mv
is a single operation that both moves the file and updates Git's index, saving you from having to run separate mv and git add/rm commands.
In summary, you should use git mv
to ensure that git
can track the changes of your files, even when they're renamed.
Now - all that git mv
/ renaming business was my first iteration of changes that I wanted to implement.
The next change I made was actually quite small in comparison.
All I did with my next change was extract a function
, based on the guidelines of this website here: https://refactoring.com/catalog/extractFunction.html.
This is what I had before I extracted my function:
This is what I had after I extracted my function:
With regards to this particular refactoring
of my code, I was actually reluctant to do it, since in my eyes, the original logic was actually clearer to me than the refactored version. Not that I find extracting functions
inherently wrong, but more so because for this particular case, there was no reusability
factor in it.
To give you a clearer idea of what I mean by reusability
, let me show you an example of what I've seen in the Visual Studio Code
repository.
In one of their files, src/vs/base/common/arrays.ts
, they define the function, sortedDiff
which defines a sub-function within it.
I think this would be a better example of when extracting a function
as described in https://refactoring.com/catalog/extractFunction.html would be appropriate.
Check out the code below:
In this example, the sub-function (pushSplice
) not only serves as a way of documenting certain steps - it also serves as logic that can be repeated within the outer function (sortedDiff
).
This is when I would think it'd be more appropriate to extract a function - but others may think differently! For others, a function is worth more than comments explaining the code - I can also agree with that standpoint too.
But without going any further into that topic, let me show you the third change I made, where I took some common logic between 2 similar files, and placed it in another file. Below, is the extracted logic, placed into a new file:
Below, you can see how my existing files benefited from this new file (there's a lot less code now in both files)!
With that, I have refactored my code in 3 different ways!
But wait - that's not the end of the lab just yet.
With each change I made to my repository for the purpose of refactoring, I have formalized those changes with git commits
.
However, there are times when you need to know how to squash
multiple commits into one, so you don't bloat the history of your git repository.
Let's demonstrate how we can use the git rebase -i
command (the i
stands for interactive
) to squash
our commits.
Running git rebase main -i
will open up a text editor
, such that you can tell git
how to handle your commit history - by default, this opens up vim
.
Let's actually edit the file, such that we can tell git
we want to squash our commits into one commit.
NOTE:
- to exit
vim
and save your changes:- press
ESC
- then
:wq
- press
ENTER
- press
- to exit
vim
without saving changes:- press
ESC
- press
:q!
- press ENTER
- press
For me, I saved my changes and was greeted with this other interactive screen, which shows you which commits you're squashing:
You'll also need to save
and exit this screen too.
After doing so, you'll be greeted with something like this:
Let's run git log --all --graph
, to see our changes visually.
You'll notice the squashed commit's commit message doesn't look very pretty.
Let's make an amended commit
, via git commit --amend
. Below, you can see I've adjusted the commit message a bit.
Let's save, and get out of the vim
.
After making adjustments to our commit history, we end up with something like this:
Now, let's switch to our local main branch and merge the changes from our refactor
branch by running the following:
git checkout main
# We should be able to do a simple fast-forward merge
git merge --ff-only refactor
Now, let me push my changes to my main branch on GitHub (normally I wouldn't do this, but this is for demonstration!).
With that, my commit was pushed to GitHub!
So to summarize, this week was a lot of fun, where we worked on making am minimum of 3 changes (where each change involved refactoring our code), and using git rebase --i main
to squash our commits.
I hope the pictures and narrative helped!
Cheers!
Top comments (0)