👥 Developer Cookbook - FASE 8: Liderazgo y Mentoría

Code reviews constructivos, pair programming, mentoría y gestión de conflictos técnicos


Receta 8.5: Code Reviews Constructivos

¿Qué es? Revisar código de otros de forma que ayude al equipo a mejorar, no solo encontrar errores.

Goals de un code review:

  1. Catch bugs antes de producción
  2. Mantener code quality
  3. Compartir conocimiento
  4. Mejorar habilidades del team
  5. NOT: Demostrar que eres más inteligente

Framework de code review:

class CodeReviewGuidelines:
    """Guidelines para code reviews efectivos"""

    @staticmethod
    def what_to_review():
        """Qué revisar en orden de importancia"""

        checklist = """
        CODE REVIEW CHECKLIST
        =====================

        Priority 1: CORRECTNESS (Must fix)
        - Does it work?
        - Are there bugs?
        - Edge cases handled?
        - Error handling present?

        Priority 2: SECURITY (Must fix)
        - SQL injection risks?
        - XSS vulnerabilities?
        - Auth/authorization correct?
        - Secrets hardcoded?
        - Input validation?

        Priority 3: PERFORMANCE (Fix if significant)
        - N+1 queries?
        - Unnecessary loops?
        - Memory leaks?
        - Blocking operations?

        Priority 4: READABILITY (Suggest improvements)
        - Clear variable names?
        - Functions < 50 lines?
        - Comments for complex logic?
        - Consistent style?

        Priority 5: DESIGN (Suggest if major issue)
        - Follows SOLID principles?
        - Appropriate abstraction?
        - Code duplication?
        - Testable?

        Priority 6: NITPICKS (Optional)
        - Formatting (let linter handle)
        - Naming preferences
        - Minor refactors

        What NOT to review:
        - Personal style preferences
        - Things the linter catches
        - Entire architecture (that's ADR territory)
        - Unrelated code in the same file
        """
        print(checklist)

    @staticmethod
    def how_to_give_feedback():
        """Cómo dar feedback constructivo"""

        # 1. BE SPECIFIC, NOT VAGUE
        # BAD: "This function is bad"
        # GOOD: "This function does 3 things. Consider splitting into:
        #         validateInput(), processData(), saveToDatabase()"
        #
        # 2. EXPLAIN WHY, NOT JUST WHAT
        # BAD: "Use const instead of let"
        # GOOD: "Use const instead of let. This prevents accidental
        #        reassignment and makes the code more predictable."
        #
        # 3. ASK QUESTIONS, DON'T DEMAND
        # BAD: "Change this to use a Set"
        # GOOD: "Have you considered using a Set? It would be O(1)
        #        lookup instead of O(n) with an array."
        #
        # 4. OFFER SOLUTIONS, NOT JUST PROBLEMS
        # BAD: "This has a memory leak"
        # GOOD: "This has a memory leak because we're not clearing
        #        the interval. Add: clearInterval(intervalId)"
        #
        # 5. PRAISE GOOD CODE
        # "Nice! This error handling is very thorough."
        # "Good catch on the edge case with empty strings."
        pass

    @staticmethod
    def severity_labels():
        """Usar labels para severidad"""

        # BLOCKING - Must fix before merge
        # "🔴 [BLOCKING] SQL injection vulnerability. Use parameterized queries."
        #
        # SUGGESTION - Nice to have, not required
        # "🟡 [SUGGESTION] Consider extracting this to a helper function."
        #
        # QUESTION - Asking for clarification
        # "💡 [QUESTION] Why are we using a timeout here?"
        #
        # NITPICK - Purely stylistic
        # "🎨 [NITPICK] Extra whitespace (but linter should catch this)"
        pass

    @staticmethod
    def how_to_receive_feedback():
        """Cómo recibir feedback (for the author)"""

        guidelines = """
        HOW TO RECEIVE FEEDBACK
        =======================

        1. DON'T TAKE IT PERSONALLY
        - Code review != personal attack
        - Everyone's code can improve
        - Reviewers are helping you

        2. ASK CLARIFYING QUESTIONS
        - "Can you explain what you mean by 'this won't scale'?"
        - "I'm not familiar with that pattern. Can you link to docs?"
        - "Is this a blocker or a nice-to-have?"

        3. EXPLAIN YOUR REASONING (if you disagree)
        BAD: "No, this is fine"
        GOOD: "I chose this approach because we're only dealing
               with <100 items. The O(n^2) complexity isn't an issue
               here. Happy to optimize if we see it in profiling though."

        4. SAY THANKS
        - "Good catch! Fixed."
        - "Thanks for the suggestion. Refactored."
        - "I didn't know about that pattern. Learned something new!"

        5. MARK COMMENTS AS RESOLVED after addressing them

        6. DON'T GET DEFENSIVE
        BAD:  "This is how we always do it"
        BAD:  "It works, doesn't it?"
        GOOD: "Interesting point. Let me think about this."
        GOOD: "Good question. Let me clarify the requirements with the PM."
        """
        print(guidelines)

CodeReviewGuidelines.what_to_review()

Template de Pull Request:

## What
[One sentence describing what this PR does]

## Why
[Why are we making this change? Link to issue/ticket]

## How
[High-level explanation of approach]

## Testing
- [ ] Unit tests added
- [ ] Integration tests added
- [ ] Manually tested on staging
- [ ] Edge cases covered

## Screenshots (if UI change)
Before: [screenshot]
After: [screenshot]

## Checklist
- [ ] Code follows style guide
- [ ] Self-reviewed
- [ ] Commented complex logic
- [ ] Documentation updated
- [ ] No breaking changes (or documented)

## Deployment notes
[Any special steps needed for deployment?]
[Database migrations? Feature flags?]

## Related PRs
- #123 (dependency)
- #456 (follow-up work)

Code review examples:

# EXAMPLE 1: Security issue - GOOD REVIEW
"""
🔴 [BLOCKING] SQL Injection vulnerability

Current code:
    query = f"SELECT * FROM users WHERE id = {user_id}"
    db.execute(query)

Problem: If user_id = "1 OR 1=1", this returns all users.

Fix: Use parameterized queries
    query = "SELECT * FROM users WHERE id = ?"
    db.execute(query, (user_id,))

Reference: https://owasp.org/www-community/attacks/SQL_Injection
"""

# EXAMPLE 2: Performance issue - GOOD REVIEW
"""
🟡 [SUGGESTION] N+1 query problem

Current code makes 1 query + N queries:
    posts = Post.all()  # 1 query
    for post in posts:
        author = post.author  # N queries (one per post)

With 100 posts, this is 101 queries.

Suggested fix (1 query total):
    posts = Post.select_related('author').all()
    for post in posts:
        author = post.author  # No additional query

If this endpoint isn't heavily used, we can defer optimization.
What do you think?
"""

# EXAMPLE 3: Praise
"""
Nice work! A few highlights:
1. Error handling is thorough (lines 45-60)
2. Clear function names (getUserByEmail, validatePassword)
3. Good test coverage (90%+)
4. Edge cases handled (null, empty string, special chars)

One minor suggestion below, but overall this is solid code.
"""

Receta 8.6: Pair Programming & Mob Programming

¿Qué es?

Cuándo usar:

class CollaborativeCoding:
    """Guía para pair/mob programming"""

    @staticmethod
    def when_to_pair():
        """Cuándo hacer pair programming"""

        good_use_cases = [
            "Onboarding nuevo team member (pair con senior dev, aprende codebase)",
            "Complex problem solving (algoritmos, arquitectura, debugging tricky bugs)",
            "High-risk changes (seguridad, pagos, data migrations)",
            "Knowledge sharing (senior enseña a junior, domain expert comparte contexto)",
            "Unblocking someone (stuck >1 hora, necesita perspectiva nueva)"
        ]

        when_not_to_pair = [
            "Trivial tasks (formatting, typos)",
            "Well-understood CRUD operations",
            "Research/exploration (do solo, then share findings)",
            "When both people are in deep work flow",
            "When remote with poor connection"
        ]

        # ROI: Pair programming costs 2x engineering time but:
        # - 15% fewer bugs (less debugging later)
        # - 50% faster code reviews
        # - Better knowledge distribution
        # - Higher code quality
        # Net: Often breaks even or saves time long-term
        return good_use_cases, when_not_to_pair

    @staticmethod
    def roles_and_rotation():
        """Roles en pair programming"""

        # DRIVER (has keyboard)
        # - Writes the code, focuses on tactical details
        # - Verbalizes what they're doing
        # - Asks questions when stuck
        #
        # NAVIGATOR (no keyboard)
        # - Reviews code as it's written, thinks strategically
        # - Catches mistakes, suggests improvements
        # - Researches docs/Stack Overflow
        # - Does NOT micromanage every character
        #
        # ROTATION: Switch roles every 15-30 min (Pomodoro)
        # 9:00-9:25  Alice drives, Bob navigates
        # 9:25-9:30  5-min break
        # 9:30-9:55  Bob drives, Alice navigates
        # ...repeat...
        pass

    @staticmethod
    def remote_pairing_tools():
        """Tools para pair programming remoto"""

        tools = {
            "VS Code Live Share": [
                "Both can type simultaneously, each has own cursor",
                "Shared terminal and debugging session",
                "Voice chat built-in",
                "Best option for remote pairing"
            ],
            "Tuple": "Made specifically for pairing, low latency",
            "Replit": "Web-based, instant setup, zero config",
            "Gitpod": "Cloud dev environments",
        }

        remote_tips = [
            "Use high-quality microphone (not laptop mic)",
            "Minimize screen share lag (close other apps)",
            "Take breaks every 25-30 minutes",
            "Overcommunicate (can't see body language)",
            "Use 'I'm going to...' before big changes"
        ]
        return tools, remote_tips

    @staticmethod
    def mob_programming():
        """Mob programming (whole team codes together)"""

        # Roles: 1 Driver (rotates every 5-10 min) + N Navigators + 1 Facilitator
        #
        # When to mob:
        # - Architectural decisions
        # - Complex algorithm design
        # - Learning new technology as team
        # - Critical bug affecting everyone
        #
        # Rules:
        # 1. "For an idea to go from your head to the computer,
        #     it must go through someone else's hands"
        # 2. Driver only types what navigators say
        # 3. Be kind, respectful, considerate
        # 4. Everyone contributes, no side conversations
        #
        # Pros: Shared understanding, no knowledge silos,
        #       fewer meetings, higher quality, learning opportunity
        # Cons: Expensive (N engineers on 1 task), can be tiring,
        #       needs strong facilitation
        pass

CollaborativeCoding.when_to_pair()

Receta 8.7: Mentoría y Delegación

¿Qué es? Ayudar a otros a crecer técnicamente mientras liberas tu tiempo para trabajo de mayor impacto.

Framework de mentoría:

from dataclasses import dataclass
from typing import List
from enum import Enum

class SkillLevel(Enum):
    """Niveles de habilidad"""
    NOVICE = 1        # No sabe qué no sabe
    BEGINNER = 2      # Sabe qué no sabe
    INTERMEDIATE = 3  # Puede trabajar con supervisión
    ADVANCED = 4      # Puede trabajar independiente
    EXPERT = 5        # Puede enseñar a otros

@dataclass
class MentorshipGoal:
    """Goal de mentoría para un mentee"""

    skill: str
    current_level: SkillLevel
    target_level: SkillLevel
    timeline_weeks: int
    milestones: List[str]

    def create_learning_plan(self) -> dict:
        """Crear plan de aprendizaje"""

        plan = {
            'skill': self.skill,
            'current': self.current_level.name,
            'target': self.target_level.name,
            'duration': f"{self.timeline_weeks} weeks",
            'weekly_activities': []
        }

        # Novice -> Beginner
        if self.current_level == SkillLevel.NOVICE:
            plan['weekly_activities'] = [
                "Week 1-2: Read fundamental concepts (docs, tutorials)",
                "Week 3-4: Complete guided exercises",
                "Week 5-6: Build small project with code reviews",
                "Week 7-8: Pair program with mentor on real tasks"
            ]

        # Beginner -> Intermediate
        elif self.current_level == SkillLevel.BEGINNER:
            plan['weekly_activities'] = [
                "Week 1-3: Take ownership of small features",
                "Week 4-6: Fix bugs independently",
                "Week 7-9: Code reviews (reviewing others' code)",
                "Week 10-12: Lead small project end-to-end"
            ]

        # Intermediate -> Advanced
        elif self.current_level == SkillLevel.INTERMEDIATE:
            plan['weekly_activities'] = [
                "Week 1-4: Design architecture for new features",
                "Week 5-8: Mentor junior developers",
                "Week 9-12: Lead technical discussions",
                "Week 13-16: Own large project with minimal guidance"
            ]

        return plan

# Ejemplo de uso
goal = MentorshipGoal(
    skill="Backend API Development",
    current_level=SkillLevel.BEGINNER,
    target_level=SkillLevel.INTERMEDIATE,
    timeline_weeks=12,
    milestones=[
        "Build CRUD API independently",
        "Understand authentication/authorization",
        "Write integration tests",
        "Deploy to staging",
        "Handle production incident"
    ]
)

plan = goal.create_learning_plan()
print(f"Mentorship Plan: {plan['skill']}")
print(f"Current: {plan['current']} -> Target: {plan['target']}")
for activity in plan['weekly_activities']:
    print(f"  - {activity}")

Delegation framework:

class DelegationMatrix:
    """Decidir qué delegar y a quién"""

    @staticmethod
    def delegation_quadrant():
        """
        DELEGATION MATRIX

                    High Complexity
                          |
               III        |        IV
           Challenge &    |      DON'T
            Support       |    Delegate
        ------------------+-----------------
               I          |        II         High
           Delegate       |      Stretch       Stakes
           Completely     |    Assignment
                          |
                    Low Complexity

        Quadrant I (Low complexity, Low stakes): DELEGATE COMPLETELY
          - Examples: Update docs, fix typos, add logging, run tests
          - To: Junior developers

        Quadrant II (High complexity, Low stakes): STRETCH ASSIGNMENT
          - Examples: Build internal tool, refactor module, POC with new tech
          - To: Intermediate developers (growth opportunity)

        Quadrant III (Low complexity, High stakes): CHALLENGE & SUPPORT
          - Examples: Deploy to production (well-documented), hotfix (simple)
          - To: Intermediate/Advanced with close oversight

        Quadrant IV (High complexity, High stakes): DON'T DELEGATE
          - Examples: Redesign auth, DB migration (100M+ records), payment bugs
          - To: Yourself or senior engineer
        """
        pass

    @staticmethod
    def delegation_checklist():
        """Checklist al delegar"""

        before = [
            "Define success criteria: 'Task is done when X, Y, Z are complete'",
            "Estimate time required: 'This should take 2-3 days'",
            "Identify dependencies: 'You'll need access to X, approval from Y'",
            "Set deadline: 'Need this by Friday EOD'",
            "Choose right person: Match task complexity to skill level",
            "Explain WHY (not just WHAT): 'We need this because...'",
            "Provide resources: 'Check docs at X, example in Y'"
        ]

        during = [
            "Set check-in schedule: 'Let's sync daily at 2pm'",
            "Be available for questions (not: 'Figure it out yourself')",
            "Monitor progress (don't micromanage)",
            "Unblock proactively: if stuck, jump in"
        ]

        after = [
            "Review work: code review, test, validate",
            "Give specific feedback: 'This was great because...' / 'Next time, consider...'",
            "Recognize effort: public praise in team channel",
            "Document learnings: 'What would you do differently?'"
        ]

        return before, during, after

    @staticmethod
    def mentorship_1on1_template():
        """Template para 1-on-1s de mentoría (30-45 min, weekly/bi-weekly)"""

        # AGENDA:
        # 1. How are you? (5 min) - genuine check-in, not work talk
        # 2. Progress on goals (10 min)
        #    - "What did you accomplish this week?"
        #    - "What challenged you?"
        #    - "What did you learn?"
        # 3. Current work (10 min)
        #    - "Walk me through your solution"
        #    - "What other approaches did you consider?"
        # 4. Learning & growth (10 min)
        #    - "What do you want to learn next?"
        # 5. Career discussion (5 min)
        #    - "Where do you see yourself in 1 year?"
        # 6. Action items (5 min)
        #
        # NOTES:
        # - Let mentee drive conversation
        # - Ask open-ended questions
        # - Listen more than talk (70/30 rule)
        # - Take notes for continuity
        # - Follow up on action items
        pass

DelegationMatrix.delegation_quadrant()

Receta 8.8: Gestión de Conflictos Técnicos

¿Qué es? Resolver desacuerdos técnicos de forma productiva, no destructiva.

Tipos de conflictos:

class TechnicalConflictResolution:
    """Framework para resolver conflictos técnicos"""

    @staticmethod
    def types_of_conflicts():
        """Tipos comunes de conflictos técnicos"""

        types = {
            "Technology Choice": {
                "example": "React vs Vue, PostgreSQL vs MongoDB",
                "root_cause": "Different preferences/experiences",
                "resolution": "Define objective criteria, POC with both options"
            },
            "Architecture Pattern": {
                "example": "Microservices vs Monolith",
                "root_cause": "Different trade-off priorities",
                "resolution": "List pros/cons, write ADR, align on what we optimize for"
            },
            "Code Style": {
                "example": "Tabs vs spaces, naming conventions",
                "root_cause": "Personal preference",
                "resolution": "Adopt team style guide, use linter/formatter (automate it)"
            },
            "Priority Disagreement": {
                "example": "Fix tech debt vs ship features",
                "root_cause": "Different perspectives (engineering vs product)",
                "resolution": "Quantify impact, escalate to stakeholder, compromise"
            },
            "Implementation Approach": {
                "example": "How to implement feature X",
                "root_cause": "Different mental models",
                "resolution": "Whiteboard both approaches, discuss trade-offs, experiment"
            }
        }
        return types

    @staticmethod
    def resolution_framework():
        """Framework paso a paso para resolver conflictos"""

        steps = [
            "STEP 1: UNDERSTAND THE DISAGREEMENT",
            "  - What exactly do we disagree on?",
            "  - What are we each proposing?",
            "  - What problem are we trying to solve?",
            "",
            "STEP 2: IDENTIFY COMMON GROUND",
            "  - What do we agree on? (find shared values)",
            "",
            "STEP 3: ARTICULATE DIFFERENCES",
            "  - What trade-offs are we making? List pros/cons objectively.",
            "",
            "STEP 4: DEFINE DECISION CRITERIA",
            "  - Rank what matters most (e.g., speed, learning curve, cost)",
            "",
            "STEP 5: EVALUATE OPTIONS OBJECTIVELY",
            "  - Score each option against criteria with weights",
            "",
            "STEP 6: MAKE DECISION & COMMIT",
            "  - 'Disagree and commit': have strong opinions, debate,",
            "    but once decided, everyone commits 100%",
            "",
            "STEP 7: DOCUMENT & MOVE FORWARD",
            "  - Write ADR: what we decided, why, alternatives, trade-offs",
            "  - Set review point: 'Let's revisit in 6 months with data'"
        ]
        return steps

    @staticmethod
    def anti_patterns():
        """Anti-patterns en resolución de conflictos"""

        # 1. "I'M THE SENIOR, SO WE DO IT MY WAY"
        #    -> Kills discussion, juniors stop contributing
        #    Better: "In my experience Y happened. What are your thoughts?"
        #
        # 2. ANALYSIS PARALYSIS (debating forever)
        #    -> Time-box decision: "We'll decide by Friday"
        #    Or: Build POC with both, evaluate concrete code
        #
        # 3. APPEAL TO AUTHORITY
        #    "Google uses X, so we should too"
        #    -> "Google uses X because [specific reason]. Same constraint?"
        #
        # 4. BIKE-SHEDDING
        #    Spending 2 hours on variable names, ignoring architecture
        #    -> "Let's defer style to the linter. Hard question is: monolith vs microservices?"
        #
        # 5. MAKING IT PERSONAL
        #    "You always push for complex solutions"
        #    -> Focus on code/approach, not person
        #    "I'm worried this approach might be complex. Can we discuss trade-offs?"
        #
        # 6. SECRET DECISION (private, no buy-in)
        #    -> Public discussion (Slack, RFC), everyone gets to weigh in
        #
        # 7. NO DECISION ("Let's each do it our own way")
        #    -> Make a decision, even imperfect. Document it. Move forward.
        pass

    @staticmethod
    def weighted_decision_example():
        """Ejemplo de evaluación objetiva con pesos"""

        # Ejemplo: React vs Vue
        criteria = [
            ("Time to ship", 5),
            ("Learning curve", 4),
            ("Hiring", 3),
            ("Ecosystem", 3),
            ("Maintainability", 4),
        ]

        scores = {
            "React": [3, 2, 5, 5, 4],  # scores per criteria
            "Vue":   [4, 5, 3, 3, 4],
        }

        results = {}
        for option, option_scores in scores.items():
            total = sum(weight * score for (_, weight), score
                       in zip(criteria, option_scores))
            results[option] = total

        # React: 5*3 + 4*2 + 3*5 + 3*5 + 4*4 = 69
        # Vue:   5*4 + 4*5 + 3*3 + 3*3 + 4*4 = 74
        # -> Vue wins on OUR criteria
        return results

TechnicalConflictResolution.resolution_framework()