This week, I worked on refactoring my project, dev-mate-cli. As with any refactor, my primary goal was to improve the codebase by making it cleaner and more modular. However, the process didn’t stop there - I also took this opportunity to practice using Git’s interactive rebase feature to tidy up my commit history.
Before diving into Git, I wanted to focus on improving several aspects of my code:
Modularity: Breaking down large functions into smaller, reusable components.
Error Handling: Improving how the application handled different input paths.
AI Response: Refactoring how the program interacted with OpenAI's API, ensuring better separation of concerns.
Performance: Making the file handling code more efficient by introducing recursion for processing directories.
Step-by-Step Refactor
Here’s a breakdown of the steps that I took to refactor dev-mate-cli:
Move File Processing to fileHandler
My first task was to clean up the code responsible for file processing. Previously, this logic was mixed with the part of the code that handled the setup of cli program in program.ts. To improve modularity, I moved the file processing into a dedicated fileHandler module. This separated the concerns, making the code easier to maintain and test.
Make File Path Processing Recursive
Since I was already working on the file processing, I saw an opportunity to add more robustness. The function initially handled file paths but didn’t traverse directories deeply. I enhanced it by adding recursive logic to handle directories, ensuring the program processes every file within subdirectories as well.
Split AI Response Logic into Separate Functions
One function was doing too much—handling the AI request and response in one go. To simplify this, I broke it down into two functions: one for generating the response and another for handling what happens afterward. This separation of concerns not only made the code cleaner but also easier to extend in the future.
Fix Token Usage Output When Streaming Response
While streaming AI responses to the command line, I discovered that the token usage report wasn’t working as expected. This was due to incorrect handling of response chunks when streaming. I fixed the logic so that token usage would now be correctly displayed, even when data is streamed to the command line.
Add Interface and Fallback for Program Options
To ensure that the program handled missing options gracefully, I added fallback values for the model and temperature. I also created an interface to define the options type, improving type safety and making the code more predictable.
Git Rebase: Interactive Squashing
Once all the changes were made, I turned my attention to the commit history. I had made 5 separate commits, each representing a logical change. To keep the Git history clean, I decided to squash these commits into one using Git’s interactive rebase feature.
Ran git rebase main -i to start an interactive rebase with main branch.
Squashed the 5 commits into one.
Amended the commit message to summarize all the changes in a clear and concise manner using git commit --amend command.
Merged the topic branch into main branch using fast-forward merge.
Pushed the new commit to origin using git push origin main.
The interactive rebase was smooth, and I found it to be an invaluable tool for maintaining a clean project history. I also learned how important it is to commit often but keep the history readable for maintainers and collaborators.