כמעט כל 1,562 שורות הבדיקות של TopicReview נכתבו על ידי Claude. אני רק עושה review — מעל שבועיים בפרודקשן, כל הקומיטים הבאים מוסיפים בדיקות, אף אחד לא כותב אותן מחדש. הפוסט עוסק בלמה בדיקות הן יעד האצלה אידיאלי, במה להסתכל ומה להתעלם ממנו ב-review (כולל edge case אמיתי שחסר בספק), ובהגדרת ברירת המחדל של החלוקה הזו.
הפוסט הקודם ננעל במשפט "119 ספקים ירוקים" עבור TopicReview. השאלה האמיתית שבאה אחריה: מי כתב את הבדיקות האלו?
תשובה: Claude כתב כמעט את כל 1,562 שורות קוד הבדיקה. אני רק עושה review. מעל שבועיים על פרודקשן, ודפוס התחזוקה של 1,562 השורות האלו הוא רק להוסיף בדיקות חדשות, אף פעם לא לכתוב מחדש את הישנות.
הפוסט הזה עוסק בלמה בדיקות הן המועמד הכי טוב להאצלה ל-Claude, במה להסתכל ובמה להתעלם ב-review, ועד כמה החלוקה הזו מחזיקה בפועל.
בדיקות של TopicReview מפוזרות ב-7 קבצים:
spec/services/topic_review_service_spec.rb 760 שורות (88 בדיקות)
spec/requests/topic_reviews_spec.rb 281 שורות (32 בדיקות)
spec/requests/review_appeals_spec.rb 152 שורות (16 בדיקות)
spec/requests/review_votes_spec.rb 127 שורות
spec/policies/topic_review_policy_spec.rb 109 שורות
spec/jobs/close_topic_review_job_spec.rb 71 שורות (7 בדיקות)
spec/models/topic_review_spec.rb 62 שורות
───────────────────────────────────────────
1,562 שורות
מכסה ארבעה סוגי בדיקות: service (לוגיקה עסקית), request (controller + אינטגרציה), policy (הרשאות Pundit), job (משימות מתוזמנות).
הקומיט הראשון d162f1e נושא Co-Authored-By: Claude Sonnet 4.6 ומנחית 1,100+ מאותן שורות בבת אחת. כל קומיט של spec שבא אחריו הוא "Add test for..."—ולא אחד הוא refactor או rewrite:
00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
3b185da Update specs to use PROVISIONAL_PENALTY constant
סתימת פערים, לא עבודה מחדש. הפרט הזה חוזר בהמשך.
ארבע סיבות קשיחות:
1. קלט ופלט מפורשים. בדיקה בעצם היא "בהינתן מצב זה → צפה להתנהגות זו". זהו הצד החזק של Claude: תרגום מפרט ל-assertion. קוד עסקי דורש לעיתים שיקולים; בדיקות כמעט אף פעם לא.
2. מכני × נפח גבוה. describe .open! יחיד חייב לכסות "יש חברי מושבעים מתאימים / אין / אין topic / יש כבר review פעיל"—ארבעה context-ים, עם 2–5 בלוקי it בכל אחד. אדם מתחיל לקצר פינות ב-context השלישי. Claude כותב את ה-it ה-88 באותה דקדקנות כמו הראשון.
3. לולאת משוב קצרה במיוחד. כותבים בדיקה, מריצים rspec, תוך שניות יודעים אם עוברת. קוד עסקי דורש ימי שימוש ממשי עד שבעיות צפות. לולאה קצרה = כל טעות של Claude נתפסת על ידי rspec במקום—לא צריך לשמור.
4. מקבילי מטבעו. בלוקי it עצמאיים, ללא צימוד נסתר, מתרחבים בקלות. לייצר עשרות בדיקות מבודדות בבת אחת זה בדיוק הצד החזק של Claude.
זה הציר של כל החלוקה.
להתעלם:
להסתכל:
הנקודה האחרונה היא הערך האמיתי של review. Claude מכסה בדיקות "שעולות בדעתו", אבל אלו שלא עולות לא נכתבות מעצמן. כאן בדיוק נכנס ה-review האנושי—ללכת אחורה מחוקי העסק לכיסוי החסר.
פתח את תחילת describe ".open!" ב-spec/services/topic_review_service_spec.rb:
describe ".open!" do
context "when there are eligible jurors" do
# סטטוס review תקין / post under_review / assignments נוצרות / author מקבל התראה / jurors מקבלים התראה / אין פתיחה כפולה
end
context "when there are no eligible jurors" do
# review נוצר אבל assignments לא
end
context "when post has no topic" do
# מחזיר nil
end
end
נראה מקיף. אבל הכלל האמיתי של eligible_jurors ב-model מחריג שלוש קבוצות:
def eligible_jurors
excluded_ids = [ post.user_id ] + post.reports.pluck(:user_id) + review_votes.where(stage: :initial).pluck(:user_id)
User.jurors_and_judges.where.not(id: excluded_ids.uniq)
end
עכשיו הסתכל בבדיקות—איזו בדיקה טוענת ש"מחבר הפוסט לעולם לא נבחר כמושבע"?
מחפש ב-service_spec.rb וב-model_spec.rb: אין. model_spec.rb בודק רק כמה מקרים של ה-scope pending_vote_by; לא מכסה את eligible_jurors ישירות. ב-service_spec.rb יש רק הערה: # Jurors must NOT be the post author—זה ב-setup, לא assertion.
זה מה ש-review יכול לתפוס: שלושה כללי החרגה (מחבר / מדווח / כבר-הצביע-ב-initial), אף אחד לא מוגן בבדיקה. אם מישהו בעתיד יבצע refactor ל-eligible_jurors ובטעות יוריד את post.user_id מרשימת ההחרגה, כל הבדיקות הקיימות יעברו—ופרודקשן ישקוט ויאפשר למחברים לשבת במושבעים של עצמם.
Claude לא טעה—הוא בדק את מה שהתבקש לבדוק. הוא פשוט לא שאל מיוזמתו: "האם כל אחד משלושת הכללים האלה צריך כיסוי בדיקה?" השאלה הזו—מחוקים לכיסוי לאחור—היא העבודה של review.
(בהגינות: גם אני פספסתי את זה ב-review הראשון. ראיתי זאת רק באודיט שני תוך כדי כתיבת הפוסט הזה. אז גם review אינו חד-פעמי—אבל הוא עדיין פי 10 יותר טוב מלא לבצע review.)
אם "Claude כותב + אדם עושה review" היה מושלם, לא היו קומיטים חדשים של בדיקה אחרי הראשון. המציאות מעניינת יותר—סתימת פערים בלי כתיבה מחדש:
00393fc Add test for finalize! with zero votes (expired review)
3f53304 Add test for finalize! with legacy votes missing reasoning
הראשון הוא regression test שלאחר באג—e8cb2db Default to keep verdict when review expires with zero votes הוא התיקון, 00393fc הבדיקה המצורפת. אותו דפוס לשני, בעקבות abaa22e Fix CloseTopicReviewJob failing due to reasoning validation on old votes.
שני הקומיטים האלה מוכיחים שני דברים בבת אחת:
"מספיק טוב + אפשר להמשיך לטלא" הוא רף ריאלי הרבה יותר מ"מושלם". רדיפה אחרי review מושלם היא בדיוק מה שמונע ממך להעביר בדיקות ל-Claude. קבלת "מספיק טוב" היא מה שמפעיל את החלוקה.
לא כל בדיקה מתאימה להעברה מלאה:
במקרים כאלה אדם מוביל ו-Claude עוזר.
לקחת את החלוקה הזו ולהפוך לברירת מחדל שאפשר להריץ:
למתכנת יש פחות משאב נפשי לקריאת בדיקות מאשר לקריאת קוד. בדיקות חזרתיות, מכניות, מתישות והכרחיות. כל זה מתאר בדיוק את הצד החזק של Claude—הוא לא משתעמם, לא מתעייף, לא קוטע פינות ב-it ה-50.
העבודה שלך אינה "לכתוב בדיקות"—אלא "לוודא שכל חוק עסקי מכוסה בבדיקה". אחד הוא מימוש והשני שיפוט. השיפוט נשאר איתך; המימוש הולך ל-Claude.
119 ספקים / 1,562 שורות נשלחים בקומיט אחד וחיים מעל שבועיים ללא rework—לא כי אני כותב בדיקות יותר טוב, אלא כי בכלל לא כתבתי. אני רק עושה דבר אחד ש-Claude לא עושה: מחליט אילו חוקי עסק ראויים להגנה.