How to Use a Coding Tool Like an Expert

When executing multi-file refactors, I manage, restrict, and structure my local codebase context using a strict hierarchical filtering system designed to maximize accuracy and minimize token waste.

Here is exactly how I structure and execute that process:

1. Discovery and Map Generation

Before reading or editing any files, I construct an abstract map of the codebase. I do not pull full source code at this stage.
  • Directory Trees: I run a shallow directory listing to understand the project's layout.
  • Symbols and Signatures: I search for class definitions, method signatures, and export statements across the target modules.
  • Dependency Graphs: I map out how the target files import one another to identify the upstream and downstream impacts of the refactor.

2. Context Restriction (The "Need-to-Know" Filter)

To prevent token bloat and LLM confusion, I categorize files into three strict isolation zones:
  • The Edit Zone: These are the exact files that require code modifications. I ingest these files completely.
  • The Reference Zone: These are files that interact directly with the Edit Zone (e.g., types, interfaces, or parent classes). I only ingest their structural definitions, type signatures, or relevant snippets—never the full implementation.
  • The Noise Zone: Implementation details of unrelated modules, external dependencies, and build artifacts. These are completely blocked from my context.

3. Structured Multi-File Ingestion

When I load the required files into my active context window, I enforce a clean XML/Markdown boundary structure. This structure prevents me from blending different files together or hallucinating missing links:
<file path="src/user/service.ts">
// Exact, unedited source code goes here
</file>

<file path="src/user/types.ts">
// Exact, unedited source code goes here
</file>

4. Fragmented Execution (Avoiding the "All-at-Once" Trap)

Instead of rewriting five files in a single pass—which drastically increases the risk of hallucinations—I execute refactors sequentially using a strict state machine:
  1. Plan: I generate a plaintext implementation plan outlining the exact order of changes.
  2. Modify: I apply changes to one file at a time, starting from the lowest dependency (e.g., types first, then core logic, then entry points).
  3. Verify: After each file modification, I review the updated file against the rest of the context to ensure type safety and logic consistency before moving to the next file.
To see how this works in practice, here is a concrete example of a multi-file refactor where Claude utilizes custom tool skills, MCP (Model Context Protocol) servers, GitLab, and Jira to update a Python user authentication system.

The Scenario

Jira Ticket (AUTH-42): Migrating a legacy Python codebase from custom dictionary-based user sessions to structured Pydantic models across a service layer, a route layer, and tests.
📁 my-python-app/
├── 📁 src/
│   ├── auth.py         <-- Edit Zone (Route Layer)
│   ├── user_service.py <-- Edit Zone (Service Layer)
│   └── schemas.py      <-- Edit Zone (Data Types)
├── 📁 tests/
│   └── test_auth.py    <-- Edit Zone (Unit Tests)
└── main.py             <-- Reference Zone (App Initialization)

Step 1: Discovery & Map Generation

Claude receives the Jira ticket via an MCP Jira Server connection. Before reading any code, Claude invokes a custom codebase-indexer skill (via a local Filesystem/Git MCP server) to map the directory.
  • Tool Call: mcp__filesystem__list_directory(path="./src")
  • Tool Call: mcp__grep__search(pattern="def login")
  • Output Matrix: Claude maps out that auth.py imports user_service.py, which in turn relies on schemas.py. main.py initializes the app but does not manipulate user dictionaries directly.

Step 2: Context Restriction

Claude explicitly partitions the project files into the three strict isolation zones, dropping everything else to save tokens.
  • Edit Zone (Full Ingestion): schemas.py, user_service.py, auth.py, test_auth.py.
  • Reference Zone (Signatures Only): main.py (Claude uses an MCP tool to fetch only the top 10 lines showing how the app starts, ignoring the database configuration logic below it).
  • Noise Zone (Blocked): Dockerfiles, CI/CD configuration files, and unrelated business logic folders.

Step 3: Structured Multi-File Ingestion

Claude pulls the Edit Zone files into the active context window, isolating them cleanly inside explicit structural tags so the code logic never bleeds together.
<file path="src/schemas.py">
# Current legacy state
class UserSession:
    def __init__(self, data: dict):
        self.data = data
</file>

<file path="src/user_service.py">
from src.schemas import UserSession
def get_user_session(user_id: int):
    # Returns raw dict inside legacy class
    return UserSession({"id": user_id, "role": "admin"})
</file>

Step 4: Fragmented Execution & Workflow Integration

Instead of outputting all changes at once, Claude executes a sequential state machine, executing terminal commands and Git operations via MCP at each milestone.
  1. Modify Lowest Dependency (schemas.py): Claude overwrites the file to use Pydantic.
    • Tool Call: mcp__filesystem__write_file(path="src/schemas.py", text="from pydantic import BaseModel\nclass UserSession(BaseModel):\n id: int\n role: str")
  2. Modify Middle Layer (user_service.py): Claude adjusts the service layer to instantiate the new Pydantic model.
  3. Modify Top Layer & Tests (auth.py & test_auth.py): Claude updates the endpoints and rewrites the test assertions from dictionary lookups (session['id']) to object attributes (session.id).
  4. Automated Verification: Claude runs pytest via the terminal MCP server to ensure type safety and logic integrity.
    • Tool Call: mcp__terminal__execute_command(command="poetry run pytest")
  5. GitLab & Jira Update: Once tests pass 100%, Claude uses the GitLab MCP Server to create a branch, commit the changes, and push a Merge Request, concluding by updating the Jira ticket status.
    • Tool Call: mcp__gitlab__create_merge_request(branch="feature/AUTH-42-pydantic-migration", title="Refactor sessions to Pydantic")
    • Tool Call: mcp__jira__update_issue(ticket_id="AUTH-42", status="In Review", comment="Refactor complete. MR opened in GitLab.")

Here is the exact setup and configuration guide to bind Claude Desktop / Claude Code to your workflow using the official and community-standard MCP (Model Context Protocol) servers, paired with a robust Python verification stack.

Part 1: Initializing & Configuring MCP Servers

MCP configurations are stored centrally in a JSON file. If you are using Claude Desktop, this file is located at:
  • Mac/Linux: ~/.claude/mcp.json
  • Windows: %APPDATA%\Claude\mcp.json
Add the following standard configurations to your mcp.json to link Claude to your system:
{
  "mcpServers": {
    "gitlab": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://gitlab.com"
      ]
    },
    "jira": {
      "command": "npx",
      "args": [
        "-y",
        "@hainanzhao/mcp-gitlab-jira"
      ],
      "env": {
        "JIRA_HOST": "https://your-domain.atlassian.net",
        "JIRA_USER_EMAIL": "your-email@example.com",
        "JIRA_API_TOKEN": "YOUR_ATLASSIAN_API_TOKEN"
      }
    }
  }
}

Step-by-Step Initialization:

  1. GitLab Setup: The official GitLab MCP Server uses an elegant HTTP OAuth 2.0 transport flow. When Claude starts for the first time, your web browser will automatically open to securely authorize the connection directly to your GitLab account. [1]
  2. Jira Setup: Generate a Jira API token inside your Atlassian Account Settings under Security. Paste that token into the JIRA_API_TOKEN environment variable in the JSON configuration above. [2, 3]
  3. Restart Claude: Fully quit and restart Claude Desktop (or relaunch your terminal if using Claude Code via /mcp) to activate the connections. [1, 4]

Part 2: The Python Verification Stack (Pre-Push Checks)

To guarantee that Claude does not push broken code, it must use a local execution environment to test code variations before opening a GitLab Merge Request. Integrate these three core tools via your build pipeline (e.g., inside a pyproject.toml using poetry or pipenv):
ToolVerification RoleClaude Execution Command
ruffLinting & Code Style: Instantly catches syntax errors, unused imports, or bad formatting across edited files without executing code.poetry run ruff check src/
mypyStatic Type Checking: Vital when refactoring interfaces (like dictionaries to Pydantic). Catches type conflicts upstream or downstream.poetry run mypy src/
pytestFunctional Unit Testing: Executes unit tests to verify behavior and ensure that updated code matches expected outputs.poetry run pytest

Part 3: The Complete Automated Workflow

Once everything is wired together, a typical interaction loop handles the entire task seamlessly from start to finish:
[User Request] ➔ "Claude, look at ticket AUTH-42 and refactor the user session schemas."
       │
       ├── 1. Read Jira Ticket via MCP (Jira Server)
       ├── 2. Segment context into Isolation Zones (Filesystem MCP)
       ├── 3. Sequentially rewrite schemas, services, and tests (File-by-file)
       ├── 4. Trigger Pre-Push Checks:
       │      ├── ruff check (No lint errors)
       │      ├── mypy check (Type safety intact)
       │      └── pytest     (All tests passing 100%)
       │
       └── 5. Final Action:
              ├── GitLab: Commit code, create branch, and open Merge Request
              └── Jira: Progress ticket status to "In Review" and attach the MR link


Popular posts from this blog

COBOT with GenAI and Federated Learning

Self-contained Raspberry Pi surveillance System Without Continue Internet

AI in Education: Embracing Change for Future-Ready Learning