ReadmeGenie のリファクタリング

Oct 09, 2024 am 10:14 AM

Refactoring ReadmeGenie

Introduction

This week I was tasked to refactor the ReadmeGenie. If you just arrived here, ReadmeGenie is my open-source project that uses AI to generate readmes based on the files that the user inputs.

Initially, my thoughts were, "The program is working fine. I’ve been developing it in an organized way since day one... so why change it?"

Well, after taking a week-long break from the project, I opened it up again and immediately thought, "What is this?"

Why refactor?

To give you some context, here’s an example: One of my core functions, which I once thought was perfect, turned out to be much more complex than necessary. During the refactoring process, I broke it down into five separate functions—and guess what? The code is much cleaner and easier to manage now.

Take a look at the original version of this function:

def generate_readme(file_paths, api_key, base_url, output_filename, token_usage):
    try:
        load_dotenv()

        # Check if the api_key was provided either as an environment variable or as an argument
        if not api_key and not get_env():
            logger.error(f"{Fore.RED}API key is required but not provided. Exiting.{Style.RESET_ALL}")
            sys.exit(1)

        # Concatenate content from multiple files
        file_content = ""
        try:
            for file_path in file_paths:
                with open(file_path, 'r') as file:
                    file_content += file.read() + "\\n\\n"
        except FileNotFoundError as fnf_error:
            logger.error(f"{Fore.RED}File not found: {file_path}{Style.RESET_ALL}")
            sys.exit(1)

        # Get the base_url from arguments, environment, or use the default
        chosenModel = selectModel(base_url)
        try:
            if chosenModel == 'cohere':
                base_url = os.getenv("COHERE_BASE_URL", "https://api.cohere.ai/v1")
                response = cohereAPI(api_key, file_content)
                readme_content = response.generations[0].text.strip() + FOOTER_STRING
            else:
                base_url = os.getenv("GROQ_BASE_URL", "https://api.groq.com")
                response = groqAPI(api_key, base_url, file_content)
                readme_content = response.choices[0].message.content.strip() + FOOTER_STRING
        except AuthenticationError as auth_error:
            logger.error(f"{Fore.RED}Authentication failed: Invalid API key. Please check your API key and try again.{Style.RESET_ALL}")
            sys.exit(1)
        except Exception as api_error:
            logger.error(f"{Fore.RED}API request failed: {api_error}{Style.RESET_ALL}")
            sys.exit(1)

        # Process and save the generated README content
        if readme_content[0] != '*':
            readme_content = "\n".join(readme_content.split('\n')[1:])

        try:
            with open(output_filename, 'w') as output_file:
                output_file.write(readme_content)
            logger.info(f"README.md file generated and saved as {output_filename}")
            logger.warning(f"This is your file's content:\n{readme_content}")
        except IOError as io_error:
            logger.error(f"{Fore.RED}Failed to write to output file: {output_filename}. Error: {io_error}{Style.RESET_ALL}")
            sys.exit(1)

        # Save API key if needed
        if not get_env() and api_key is not None:
            logger.warning("Would you like to save your API key and base URL in a .env file for future use? [y/n]")
            answer = input()
            if answer.lower() == 'y':
                create_env(api_key, base_url, chosenModel)
        elif get_env():
            if chosenModel == 'cohere' and api_key != os.getenv("COHERE_API_KEY"):
                if api_key is not None:
                    logger.warning("Would you like to save this API Key? [y/n]")
                    answer = input()
                    if answer.lower() == 'y':
                        create_env(api_key, base_url, chosenModel)
            elif chosenModel == 'groq' and api_key != os.getenv("GROQ_API_KEY"):
                if api_key is not None:
                    logger.warning("Would you like to save this API Key? [y/n]")
                    answer = input()
                    if answer.lower() == 'y':
                        create_env(api_key, base_url, chosenModel)

        # Report token usage if the flag is set
        if token_usage:
            try:
                usage = response.usage
                logger.info(f"Token Usage Information: Prompt tokens: {usage.prompt_tokens}, Completion tokens: {usage.completion_tokens}, Total tokens: {usage.total_tokens}")
            except AttributeError:
                logger.warning(f"{Fore.YELLOW}Token usage information is not available for this response.{Style.RESET_ALL}")
        logger.info(f"{Fore.GREEN}File created successfully")
        sys.exit(0)
ログイン後にコピー

1. Eliminate Global Variables
Global variables can lead to unexpected side effects. Keep the state within the scope it belongs to, and pass values explicitly when necessary.

2. Use Functions for Calculations
Avoid storing intermediate values in variables where possible. Instead, use functions to perform calculations when needed—this keeps your code flexible and easier to debug.

3. Separate Responsibilities
A single function should do one thing, and do it well. Split tasks like command-line argument parsing, file reading, AI model management, and output generation into separate functions or classes. This separation allows for easier testing and modification in the future.

4. Improve Naming
Meaningful variable and function names are crucial. When revisiting your code after some time, clear names help you understand the flow without needing to re-learn everything.

5. Reduce Duplication
If you find yourself copying and pasting code, it’s a sign that you could benefit from shared functions or classes. Duplication makes maintenance harder, and small changes can easily result in bugs.

Commiting and pushing to GitHub

1. Create a branch
I started by creating a branch using:

git checkout -b <branch-name>
ログイン後にコピー

This command creates a new branch and switches to it.

2. Making a Series of Commits
Once on the new branch, I made incremental commits. Each commit represents a logical chunk of work, whether it was refactoring a function, fixing a bug, or adding a new feature. Making frequent, small commits helps track changes more effectively and makes it easier to review the history of the project.

git status
git add <file_name>
git commit -m "Refactored function"
ログイン後にコピー

3. Rebasing to Keep a Clean History
After making several commits, I rebased my branch to keep the history clean and linear. Rebasing allows me to reorder, combine, or modify commits before they are pushed to GitHub. This is especially useful if some of the commits are very small or if I want to avoid cluttering the commit history with too many incremental changes.

git rebase -i main
ログイン後にコピー

In this step, I initiated an interactive rebase on top of the main branch. The -i flag allows me to modify the commit history interactively. I could squash some of my smaller commits into one larger, cohesive commit. For instance, if I had a series of commits like:

Refactor part 1
Refactor part 2
Fix bug in refactor

I could squash them into a single commit with a clearer message

4. Pushing Changes to GitHub
Once I was satisfied with the commit history after the rebase, I pushed the changes to GitHub. If you’ve just created a new branch, you’ll need to push it to the remote repository with the -u flag, which sets the upstream branch for future pushes.

git push -u origin <branch-name>
ログイン後にコピー

5. Merging
In the last step I did a fast-forward merge to the main branch and pushed again

git checkout main # change to the main branch
git merge --ff-only <branch-name> # make a fast-forward merge
git push origin main # push to the main
ログイン後にコピー

Takeaways

Everything has room to improve. Refactoring may seem like a hassle, but it often results in cleaner, more maintainable, and more efficient code. So, the next time you feel hesitant about refactoring, remember: there’s always a better way to do things.
Even though I think it's perfect now, I will definitely have something to improve on my next commit.

以上がReadmeGenie のリファクタリングの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

中間の読書にどこでもfiddlerを使用するときにブラウザによって検出されないようにするにはどうすればよいですか? 中間の読書にどこでもfiddlerを使用するときにブラウザによって検出されないようにするにはどうすればよいですか? Apr 02, 2025 am 07:15 AM

fiddlereveryversings for the-middleの測定値を使用するときに検出されないようにする方法

プロジェクトの基本と問題駆動型の方法で10時間以内にコンピューター初心者プログラミングの基本を教える方法は? プロジェクトの基本と問題駆動型の方法で10時間以内にコンピューター初心者プログラミングの基本を教える方法は? Apr 02, 2025 am 07:18 AM

10時間以内にコンピューター初心者プログラミングの基本を教える方法は?コンピューター初心者にプログラミングの知識を教えるのに10時間しかない場合、何を教えることを選びますか...

Investing.comの反クローラーメカニズムをバイパスするニュースデータを取得する方法は? Investing.comの反クローラーメカニズムをバイパスするニュースデータを取得する方法は? Apr 02, 2025 am 07:03 AM

Investing.comの反クラウリング戦略を理解する多くの人々は、Investing.com(https://cn.investing.com/news/latest-news)からのニュースデータをクロールしようとします。

Python 3.6のロードピクルスファイルエラーmodulenotfounderror:ピクルスファイル「__builtin__」をロードした場合はどうすればよいですか? Python 3.6のロードピクルスファイルエラーmodulenotfounderror:ピクルスファイル「__builtin__」をロードした場合はどうすればよいですか? Apr 02, 2025 am 06:27 AM

Python 3.6のピクルスファイルの読み込みエラー:modulenotfounderror:nomodulenamed ...

Scapy Crawlerを使用するときにパイプラインファイルを書き込めない理由は何ですか? Scapy Crawlerを使用するときにパイプラインファイルを書き込めない理由は何ですか? Apr 02, 2025 am 06:45 AM

SCAPYクローラーを使用するときにパイプラインファイルを作成できない理由についての議論は、SCAPYクローラーを学習して永続的なデータストレージに使用するときに、パイプラインファイルに遭遇する可能性があります...

See all articles