今週、私は ReadmeGenie をリファクタリングするという任務を与えられました。ここに来たばかりの方のために、ReadmeGenie は、AI を使用してユーザーが入力したファイルに基づいて Readme を生成する私のオープンソース プロジェクトです。
最初、私の考えは次のようなものでした。「プログラムは正常に動作しています。最初から組織的な方法で開発してきたのに、なぜ変更する必要があるのでしょうか?」
さて、プロジェクトから 1 週間の休憩を取った後、再びプロジェクトを開いてすぐに思いつきました。「これは何だろう?」
コンテキストを説明するために、例を示します。かつては完璧だと思っていたコア機能の 1 つが、必要以上に複雑であることが判明しました。リファクタリング プロセス中に、これを 5 つの個別の関数に分割しました。どうなるでしょうか?コードがよりクリーンになり、管理が容易になりました。
この関数の元のバージョンを見てください:
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.グローバル変数の削除
グローバル変数は予期しない副作用を引き起こす可能性があります。状態をそれが属するスコープ内に保持し、必要に応じて値を明示的に渡します。
2.計算に関数を使用する
可能な限り、中間値を変数に格納することは避けてください。代わりに、必要に応じて関数を使用して計算を実行します。これにより、コードが柔軟になり、デバッグが容易になります。
3.個別の責任
単一の関数は 1 つのことを実行し、それを適切に実行する必要があります。コマンドライン引数の解析、ファイルの読み取り、AI モデルの管理、出力の生成などのタスクを別の関数またはクラスに分割します。この分離により、将来のテストと変更が容易になります。
4.ネーミングを改善
意味のある変数名と関数名は非常に重要です。しばらくしてからコードを再検討するとき、明確な名前は、すべてを再学習する必要なくフローを理解するのに役立ちます。
5.重複を減らす
コードをコピーして貼り付けていることに気づいたら、それは共有関数やクラスのメリットが得られる可能性があることを示しています。重複するとメンテナンスが難しくなり、小さな変更によってバグが発生しやすくなります。
1.ブランチを作成
まず、
を使用してブランチを作成しました。
git checkout -b
このコマンドは新しいブランチを作成し、それに切り替えます。
2.一連のコミットの作成
新しいブランチに到達したら、増分コミットを作成しました。各コミットは、関数のリファクタリング、バグの修正、新機能の追加など、作業の論理的な塊を表します。小規模なコミットを頻繁に行うと、変更をより効果的に追跡し、プロジェクトの履歴を確認しやすくなります。
git status git addgit commit -m "Refactored function"
3.クリーンな履歴を維持するためのリベース
いくつかのコミットを行った後、履歴をクリーンで直線的に保つためにブランチをリベースしました。リベースを使用すると、コミットを GitHub にプッシュする前に並べ替え、結合、または変更できます。これは、一部のコミットが非常に小さい場合、またはあまりにも多くの増分変更によってコミット履歴が乱雑になることを避けたい場合に特に便利です。
git rebase -i main
このステップでは、メイン ブランチ上で対話型リベースを開始しました。 -i フラグを使用すると、コミット履歴を対話的に変更できます。いくつかの小さなコミットを 1 つのより大きな、まとまったコミットにまとめることができました。たとえば、次のような一連のコミットがあるとします。
リファクタリング パート 1
リファクタリング パート 2
リファクタリングのバグを修正
より明確なメッセージを含む単一のコミットにまとめることもできました
4. GitHub への変更のプッシュ
リベース後のコミット履歴に満足したら、変更を GitHub にプッシュしました。新しいブランチを作成したばかりの場合は、-u フラグを使用してそれをリモート リポジトリにプッシュする必要があります。これにより、今後のプッシュ用に上流ブランチが設定されます。
git push -u origin
5.結合
最後のステップで、メインブランチへの早送りマージを実行し、再度プッシュしました
git checkout main # change to the main branch git merge --ff-only# make a fast-forward merge git push origin main # push to the main
すべてに改善の余地があります。リファクタリングは面倒に思えるかもしれませんが、多くの場合、よりクリーンで保守しやすく効率的なコードが得られます。したがって、次回リファクタリングに躊躇するときは、もっと良い方法が常にあるということを思い出してください。
今は完璧だと思いますが、次のコミットでは間違いなく改善すべき点があるでしょう。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3