זרימת רפקטורינג (Refactor Flow)
סקירה כללית
מנוע הרפקטורינג מאפשר שינוי מבנה קוד בצורה בטוחה עם אימות לפני ואחרי.
סוגי רפקטורינג
סוג |
Enum |
תיאור |
|---|---|---|
Split Functions |
|
פיצול קובץ גדול לפונקציות נפרדות |
Extract Functions |
|
חילוץ קוד חוזר לפונקציות |
Merge Similar |
|
מיזוג קוד דומה (DRY) |
Convert to Classes |
|
המרה למחלקות (OOP) |
Dependency Injection |
|
הוספת DI לשיפור בדיקות |
זרימת עבודה
sequenceDiagram
participant U as User
participant B as Bot
participant H as Refactor Handler
participant RE as RefactoringEngine
participant CA as CodeAnalyzer
participant CS as CodeService
participant DB as MongoDB
U->>B: /refactor <filename>
B->>H: handle_refactor()
H->>DB: שליפת קובץ
DB-->>H: file_data
H->>U: תפריט בחירת סוג רפקטורינג
U->>H: בחירת סוג
H->>RE: refactor(file_data, refactor_type)
RE->>CA: CodeAnalyzer(code, filename)
CA->>CA: analyze() - AST parsing
CA->>CA: _extract_functions()
CA->>CA: _extract_classes()
CA->>CA: _calculate_dependencies()
CA-->>RE: functions, classes, dependencies
RE->>RE: יצירת הצעת רפקטורינג
alt SPLIT_FUNCTIONS
RE->>RE: Smart Clustering (קבוצות דומיין) + Coupling
RE->>RE: Dry-Run Import Graph (Tarjan SCC)
RE->>RE: Merge within cycle + clean self-import + update __init__
else EXTRACT_FUNCTIONS
RE->>RE: _extract_duplicate_code()
else MERGE_SIMILAR
RE->>RE: _merge_similar_functions()
else CONVERT_TO_CLASSES
RE->>RE: _convert_to_classes()
else DEPENDENCY_INJECTION
RE->>RE: _add_dependency_injection()
else SPECIAL: models.py (Safe Decomposition)
RE->>RE: זיהוי מונולית מחלקות בלבד (ללא פונקציות)
RE->>RE: קיבוץ מחלקות לדומיינים: core/billing/inventory/(network/workflows)
RE->>RE: יצירת files תחת models/: core.py, billing.py, inventory.py, ...
RE->>RE: הזרקת יבוא בין-מודולי (למשל billing→core: from .core import User)
RE->>RE: בניית models/__init__.py עם re-exports
RE->>RE: Dry-Run Tarjan SCC בתוך models/ בלבד; מיזוג נקודתי אם נדרש והעדכון של __init__.py
end
RE-->>H: RefactorProposal
H->>CS: validate_code_input(proposed_code)
CS-->>H: validation_result
alt validation passed
H->>U: תצוגה מקדימה + אישור/עריכה/ביטול
U->>H: אישור
H->>DB: שמירת קוד מרופקטר
H->>DB: יצירת גרסה חדשה
H->>U: "רפקטורינג הושלם בהצלחה"
else validation failed
H->>U: "שגיאת אימות: {error}"
end
שימוש בבוט (Telegram)
הפקודה /refactor מופעלת ישירות בבוט טלגרם ומאפשרת לבצע רפקטורינג מונחה-מנוע לקובץ נתון.
סינטקס:
/refactor <filename>– פותח תפריט בחירת סוג רפקטורינג עבור הקובץ/refactor <type> <filename>– דילוג על התפריט (כאשר סוג נתמך)
סוגים נתמכים בפקודה:
split_functions– 📦 פיצול קובץ גדול לפי דומיין/קוהזיהextract_functions– 🔧 חילוץ קוד חוזר לפונקציות עזרconvert_to_classes– 🎨 המרה למחלקות (OOP)merge_similar– 🔀 מיזוג קוד דומה (ניסיוני)dependency_injection– 💉 הוספת DI (ניסיוני)
התנהגות קלט שם קובץ:
תומך בשם בסיסי או נתיב מלא (matching לפי basename)
מתעלם מסימני גרש/Backticks כולל Smart Quotes:
"file.py",'file.py',`file.py``מנרמל רווח קשיח (NBSP) ותווי פורמט בלתי-נראים
התאמה לא תלויה רישיות
כולל תמיכה בקבצים גדולים (Large Files)
אם לא נמצא הקובץ – תתקבל הודעה ידידותית והמלצה להשתמש ב-
/list
תפריט אפשרויות והרצה:
לאחר בחירת סוג הרפקטורינג, המערכת מנתחת את הקוד ומייצרת הצעה (RefactorProposal)
מתקבלת הודעה מרוכזת הכוללת: - תיאור ההצעה - סיכום שינויים (Changes Summary) - אזהרות אפשריות - סטטוס אימות בסיסי (בדיקת AST לכל קובץ שנוצר)
כפתורים זמינים: -
✅ אשר ושמור– שומר את הקבצים שנוצרו -🐙 ייצוא ל-Gist– יצוא מרובה-קבצים ל-GitHub Gist -📄 תצוגה מקדימה– צפייה בתוכן הקבצים שייווצרו -📝 ערוך הצעה– שמור (כרגע במצב מוקפא, תופיע הערה ידידותית) -❌ בטל– ביטול ההצעה
שמירה ומטא-דאטה:
בעת שמירה, לכל קובץ חדש מתווספת תגית:
refactored_<type>(למשל:refactored_split_functions)נשמר רשומה בטבלה/אוסף
refactoringsעם:user_id,timestamp,refactor_type,original_file,new_files,changes_summaryניתן למצוא את הקבצים החדשים דרך
/listולנהל גרסאות כרגיל
דוגמאות:
/refactor large_module.py
# הבוט יציג תפריט סוגי רפקטורינג עבור הקובץ ויפיק הצעה
/refactor split_functions services/payments_monolith.py
# דילוג על התפריט והרצה ישירה של פיצול לפי קוהזיה
CodeAnalyzer
המנתח משתמש ב-AST (Abstract Syntax Tree) לניתוח קוד Python:
class CodeAnalyzer:
def __init__(self, code: str, filename: str = "unknown.py"):
self.code = code
self.filename = filename
self.tree: Optional[ast.AST] = None
self.functions: List[FunctionInfo] = []
self.classes: List[ClassInfo] = []
self.imports: List[str] = []
self.global_vars: List[str] = []
def analyze(self) -> bool:
try:
self.tree = ast.parse(self.code)
self._extract_imports()
self._extract_functions()
self._extract_classes()
self._extract_globals()
self._calculate_dependencies()
return True
except SyntaxError as e:
logger.error(f"שגיאת תחביר: {e}")
return False
מידע שנאסף:
@dataclass
class FunctionInfo:
name: str
start_line: int
end_line: int
args: List[str]
returns: Optional[str]
decorators: List[str]
docstring: Optional[str]
calls: Set[str] # פונקציות שקוראות לה
called_by: Set[str] # פונקציות שהיא קוראת להן
code: str
complexity: int # מורכבות ציקלומטית
יצירת הצעת רפקטורינג
@dataclass
class RefactorProposal:
refactor_type: RefactorType
original_file: str
new_files: Dict[str, str] # שם קובץ -> תוכן
description: str
changes_summary: List[str]
warnings: List[str] = field(default_factory=list)
imports_needed: Dict[str, List[str]] = field(default_factory=dict)
דוגמה - פיצול קובץ גדול:
def _split_large_file(
self,
analyzer: CodeAnalyzer,
max_functions_per_file: int = 5
) -> RefactorProposal:
# חלוקת פונקציות לקבוצות
function_groups = []
current_group = []
for func in analyzer.functions:
current_group.append(func)
if len(current_group) >= max_functions_per_file:
function_groups.append(current_group)
current_group = []
if current_group:
function_groups.append(current_group)
# יצירת קבצים חדשים
new_files = {}
base_name = Path(analyzer.filename).stem
for i, group in enumerate(function_groups):
file_content = self._generate_file_content(group, analyzer.imports)
new_files[f"{base_name}_part{i+1}.py"] = file_content
return RefactorProposal(
refactor_type=RefactorType.SPLIT_FUNCTIONS,
original_file=analyzer.filename,
new_files=new_files,
description=f"פיצול ל-{len(new_files)} קבצים",
changes_summary=[f"נוצרו {len(new_files)} קבצים חדשים"]
)
אימות לפני ואחרי
אימות לפני רפקטורינג:
# אימות הקוד המקורי תקין
original_valid = await code_service.validate_code_input(
original_code,
filename,
user_id
)
אימות אחרי רפקטורינג:
# אימות כל הקבצים החדשים
for new_filename, new_code in proposal.new_files.items():
validation = await code_service.validate_code_input(
new_code,
new_filename,
user_id
)
if not validation['valid']:
return RefactorResult(
success=False,
error=f"שגיאת אימות ב-{new_filename}: {validation['error']}"
)
ייצוא לגיסט (אופציונלי)
לאחר שהמשתמש בחן את ההצעה, הוא יכול לשתף את הקבצים שנוצרו לפני שלב השמירה:
הכפתור
🐙 ייצוא ל-Gistמופיע לצד ״✅ אשר ושמור״ בחלון ההצעה.לחיצה עליו מייצרת Gist מרובה-קבצים דרך
gist_integration.create_gist_multiעם תיאור שמציין את סוג הרפקטורינג ואת מספר הקבצים.הפיצ’ר זמין רק כאשר מוגדר
GITHUB_TOKENתקין; אחרת מוצגת הודעה שהייצוא אינו זמין והמערכת אינה מבצעת קריאה ל-GitHub.הייצוא אינו שומר את הקבצים בבסיס הנתונים ואינו מסיר את ההצעה מרשימת ההמתנה, כך שניתן לייצא ואז לאשר/לבטל בנפרד.
כישלון ביצירת ה-Gist (תקשורת, הגבלה, וכו«) נרשם בלוגים ומוצגת למשתמש הודעת שגיאה ידידותית עם הנחיה לנסות שוב מאוחר יותר.
שמירת גרסה
לאחר רפקטורינג מוצלח:
הקוד המקורי נשמר כ-gרסה קודמת
הקוד המרופקטר נשמר כגרסה חדשה
מספר הגרסה מוגדל
נוצר log entry ב-Observability
# שמירת גרסה חדשה
await db.save_file_version(
file_id=file_id,
code=refactored_code,
version=current_version + 1
)
Edge Cases
קוד לא תקין תחבירית: - הניתוח נכשל - המשתמש מקבל הודעת שגיאה - הרפקטורינג לא מתבצע
קובץ קטן מדי: - חלק מסוגי הרפקטורינג לא רלוונטיים - המשתמש מקבל הודעה
תלויות מורכבות: - המערכת מזהירה על תלויות שעלולות להישבר - המשתמש יכול לבחור להמשיך או לבטל
שגיאת אימות: - הקוד המרופקטר לא נשמר - המשתמש מקבל הסבר על השגיאה - הקוד המקורי נשאר ללא שינוי
Rollback:
- אם המשתמש לא מרוצה, יכול לשחזר גרסה קודמת
- דרך /versions <filename>
מדיניות קיבוץ ופיצול (Cohesion Policy)
המנוע מיישם מדיניות עקבית שמטרתה להימנע מ-Oversplitting בפיצול קבצים ולהימנע מ-God Class בהמרה ל-OOP:
קיבוץ לפי דומיין: הפונקציות מסווגות לקבוצות לוגיות בסיסיות:
io: פונקציות קריאה/כתיבה/תקשורת (load/save/fetch/read/write/open/connect/request וכו«).
helpers: פונקציות עזר/פורמט/נירמול/ולידציה (helper/utils/format/convert/parse/normalize/validate).
compute: לוגיקה חישובית/עסקית.
תת-קיבוץ לפי prefix: בתוך כל דומיין מתבצע פירוק נוסף לפי prefix של שם הפונקציה, כאשר רק תת-קבוצות משמעותיות נשמרות.
מיזוג לפי תלות (Affinity): קבוצות קטנות או קרובות ממוזגות על בסיס דמיון שמות וקשרי קריאה ביניהן.
הגבלת מספר קבוצות: יעד של 3–5 מודולים/מחלקות. לעולם לא מפצלים 1‑ל‑1 לכל פונקציה.
סף מינימלי לקבוצה: לא נוצרת קבוצה עבור פונקציה בודדת; קבוצות קטנות יתמזגו לקבוצות קרובות.
מניעת God Class: בעת המרה ל-OOP, אם מתקבלת קבוצה יחידה גדולה – מתבצע פיצול לפי דומיין למחלקות נפרדות (3–5).
Smart Clustering ו‑Cycle Guard
Smart Clustering:
זיהוי ”קישורים חלשים“ בין דומיינים לצורך פיצול לתת‑גרפים עצמאיים (למשל
inventory.pyו‑network.py).Coupling Rule: מנהל+ישות בצימוד גבוה יושבים יחד באותו קובץ (Collocation). אם ישנם ריבוי ישויות או העדפת שכבות – ראו מצב שכבות.
Dry‑Run Cycle Guard:
נבנה גרף ייבוא בין הקבצים שנוצרו; מזוהים מעגלים באמצעות Tarjan SCC.
פירוק מעגלים נעשה ע“י מיזוג ממוקד של זוגות מתוך ה‑SCC בלבד, כולל ניקוי self‑import ועדכון
__init__.py.ההצעה כוללת אזהרת ״פורקה תלות מעגלית״ כאשר הדבר התרחש.
Safe Decomposition ל‑models.py
כאשר קובץ הקלט הוא models.py הכולל מחלקות בלבד (אין פונקציות טופ‑לבל), נבחר מסלול פיצול בטוח:
יצירת חבילת משנה
models/עם מודולים דומייניים:core.py,billing.py,inventory.py(ולפי צורךnetwork.py/workflows.py).הזרקת יבוא בין‑מודולי למחלקות נדרשות (לדוגמה:
from .core import Userבתוךbilling.py).יצירת
models/__init__.pyשמייצא את כל הסימבולים לשמירת תאימות ייבוא קיימת.Dry‑Run לזיהוי מעגליות בתוך
models/בלבד, כולל מיזוג נקודתי ועדכון__init__.py.
שמות קבצים דומייניים (Canonical)
כדי לשמור יציבות וקריאות, שמות הקבצים הדומייניים הם:
users.py,finance.py,inventory.py,network.py(API clients),workflows.pyדומיינים נוספים:
<base>_<group>.py(למשל:split_test_big_file_analytics.py)
מצב שכבות (Layered)
כאשר נדרש בידוד שכבה של ישויות (Entities) ללא תלות חזרה בשירותים:
כל המחלקות נוצרות ב‑Leaf יחיד:
models.py.מודולי הדומיין מייבאים ממנו רק את המחלקות הדרושות.
הפעלה באמצעות משתנה סביבה:
export REFACTOR_LAYERED_MODE=1
פרמטרים ניתנים לכוונון
הפרמטרים הבאים זמינים ב-RefactoringEngine לשליטה בהתנהגות (ברירות המחדל מכוונות ליצירת 3–5 מודולים/מחלקות):
preferred_min_groups: מינימום מועדף לקבוצות משמעותיות (ברירת מחדל: 3)preferred_max_groups: מקסימום מועדף לקבוצות (ברירת מחדל: 5)absolute_max_groups: תקרה קשיחה לקבוצות במקרים קיצוניים (ברירת מחדל: 8)min_functions_per_group: סף מינימלי לפונקציות בקבוצה כדי שתצדיק מודול/מחלקה (ברירת מחדל: 2)
מגבלות ידועות
הסיווג לדומיינים מבוסס יוריסטיקה על שם הפונקציה והקריאות שלה, ולא על הבנת דומיין מלאה.
אין כרגע ניתוח side-effects או state עמוק; איחוד state משותף נעשה ידנית לפי צורך.
MERGE_SIMILARו-DEPENDENCY_INJECTIONטרם הושלמו במלואם – תופיע הודעת שגיאה ידידותית כאשר יופעלו.EXTRACT_FUNCTIONSמחלץ כפילויות רק כאשר זוהו דפוסים רלוונטיים; אם לא נמצאו, תוחזר הודעת הסבר מתאימה.