Resolving Stubborn Bugs in Minutes: A Senior Developer’s Playbook with AI Coding Assistants
This article explores how to use AI coding assistants to resolve complex, persistent software bugs efficiently. It outlines a structured workflow covering problem definition, intent comprehension, minimal reproduction path creation, test-driven fixes, and impact analysis, helping developers restore context quickly and build a verifiable engineering process for long-term maintenance.
I. Background: A Long-Standing Engineering Challenge
In software development and system maintenance, certain issues consume significantly more energy than standard feature development. These are “stubborn bugs.” They typically share several characteristics: unstable reproduction paths, involvement of multiple modules or legacy logic, lack of comprehensive test coverage, and indirect error messages. Resolving these issues traditionally relies heavily on a developer’s long-term memory of the business context and code structure, requiring repeated interruptions to current tasks for deep-dive investigations.
I recently encountered a similar legacy issue in a content distribution tool project. This tool transforms, routes, and publishes content based on predefined rules. Under specific conditions, a batch task would experience an inconsistent state write-back. This defect had existed for some time. Although previous rounds of fixes were attempted, the problem remained unresolved due to numerous logic branches and counter-intuitive exception trigger paths.
The real difficulty with such engineering problems usually lies not in a single line of incorrect code, but in the fact that troubleshooting relies heavily on complete context. It requires understanding task state transitions, event write-back mechanisms, exception handling logic, and the execution order under different input combinations. Missing context in any of these links can lead to incorrect conclusions.
II. The Fix Process: From Reproduction to Verification
2.1 Define Inputs and Expected Outputs
Before diving into the fix, the first step is to transform the “fix task” into an actionable input. This input should not merely state “there is a problem here.” Instead, it should include as much of the following information as possible:
- The business module and operation path where the problem occurs;
- The discrepancy between expected and actual behavior;
- Previously explored troubleshooting directions;
- Key data structures, state enumerations, and related API names;
- Minimal reproduction steps or sample data.
For instance, this specific issue can be described as follows: When a batch task reaches the “content transformation complete, awaiting write-back result” stage, if the remote service returns a timeout, the local state has the opportunity to be incorrectly updated from “Processing” to “Completed,” even though the actual content was not successfully delivered. The scheduler then assumes the task is finished and no longer triggers the compensation logic.
By structuring the problem this way, it transitions from a vague phenomenon to a clearly bounded execution path, facilitating subsequent context retrieval and logical inference.
2.2 Let the AI Coding Assistant Understand Intent Before Coding
After providing the complete description, an effective practice is to refrain from asking the model to output modified code immediately. Instead, ask the model to provide its understanding of the problem, potential call chains, state change points, and a verification plan. This ensures the model genuinely grasps the business semantics and reduces the probability of generating irrelevant modifications.
For example, you could initiate the request like this:
Please read the following problem description without modifying any code. List the code paths you believe might cause this state inconsistency, and explain your reasoning.
The goal here is not to get the final solution instantly, but to establish a shared context. The path analysis provided by the model often helps developers confirm whether their troubleshooting direction is accurate. It can also highlight overlooked branches, such as asynchronous callbacks, event triggers, or scheduled task fallback logic.
2.3 Compress the Problem Space with a Minimal Reproduction Path
After obtaining preliminary analysis, the next step is narrowing down the problem scope. Stubborn bugs are often difficult to resolve because they are buried under too much irrelevant context. You can compress the problem space using the following methods:
- Retain only the core modules related to the task state transition;
- Exclude configurations and external dependencies unrelated to this issue;
- Construct a minimal sample request that exclusively triggers the failure branch;
- Log every state transition step, rather than just looking at the final result.
In this specific fix, by forcing the remote service to consistently return a timeout error, the batch task reliably entered the exception branch. The logs revealed that during exception handling, the local state first entered an intermediate state, which was then overwritten to a “Completed” state by a general update logic. This update logic was originally intended for normal completion scenarios, but due to a missing check on the exception count, an incorrect state was written.
This reproduction result became the critical basis for subsequent modifications. Without this step, the fix might have simply added a defensive check without eradicating the root cause of the error.
2.4 Modification Strategy: Tests Before Implementation
Many bug fixes fail because the actual impact of the modification cannot be verified. An AI coding assistant can help developers generate targeted tests, provided that clear trigger conditions and expected state transitions are supplied.
In this practice, the following sequence was adopted:
- Have the model generate a minimal failing test case based on the reproduction path;
- Confirm that this test case genuinely fails against the current code;
- Generate the fix code;
- Run both the existing test cases and the newly added ones;
- Finally, audit similar branches for comparable issues.
An illustrative test structure looks like this:
1. Construct a batch task with a target content count of 12. Simulate the remote service triggering a timeout exception during the return phase3. Execute the task processing function4. Assert that the task status remains 'Processing'5. Assert that the exception count has incremented6. Assert that a pending compensation record exists
By following a test-first, implementation-second workflow, you effectively avoid the risk of “fixing this scenario but breaking other normal paths.” Even without a comprehensive testing framework, you can use unit tests or temporary scripts to cover critical state machines.
2.5 Ask the Model to Explain the Impact Surface
After generating the modified code, it is not recommended to submit it immediately. You can continue to ask the model for an impact surface analysis, for example:
Please explain which normal execution paths this modification affects, whether there are any side effects, and provide a list of regression focus areas.
The model can provide analysis based on the code structure, such as identifying which modules read this field, which scheduling logic uses this state value, and whether the exception branch has other entry points. While this analysis cannot replace a real code review, it helps developers quickly locate high-risk areas before submitting.
III. The Core Skill: From “Writing Code” to “Solving Problems”
My biggest takeaway from this practice is that the value of AI coding tools is not merely generating code. They help developers quickly restore context, establish reproduction paths, and verify fix effects within complex business logic. This is especially true when maintaining legacy projects, where context is rarely found in documentation but is instead scattered across historical commits, function calls, and state transitions.
Therefore, developers must cultivate a new ability: transforming vague problems into structured tasks and utilizing tools to progressively advance reasoning. This requires a solid understanding of both code and business, alongside proficiency in prompt organization, verification path design, and boundary condition mapping.
3.1 Structured Prompt Organization
An effective prompt typically includes background, objective, constraints, existing information, and expected output. For bug fixes, you can refer to the following structure:
- Problem Background: In which system and business module it occurs;
- Phenomenon Description: What operation leads to what result, and the discrepancy from the expectation;
- Troubleshooting Done: Confirming whether certain modules have been ruled out;
- Constraints: Which code cannot be changed, and which behaviors must remain backward-compatible;
- Expected Output: Whether to analyze first, generate tests first, or directly provide a fix.
This approach yields much more controllable results than simply asking, “Help me fix a bug.”
3.2 Context Control and Minimization
Large projects are not suitable for submitting the entire codebase to the model at once. You should select localized contexts based on the problem domain, such as relevant files, state enumerations, entry functions, and log snippets. You can have the model analyze a specific function or module first, then gradually expand to upstream and downstream components. If the model’s output is unexpected, prioritize checking whether necessary background information is missing rather than simply dismissing the tool’s capability.
3.3 The Importance of a Verification Loop
No matter how small the modification, it must form a verification loop. At a minimum, you must answer three questions:
- Can the original problem be consistently reproduced?
- Does the original problem disappear after the modification?
- Are normal paths left undamaged?
A fix is only truly complete when these three points are addressed. Otherwise, it may appear resolved, but in reality, only the trigger conditions have shifted, and the issue will resurface later.
IV. Improving Efficiency in Long-Term Maintenance
In long-term projects, stubborn bugs are not rare events. Rather than passively troubleshooting each time, you can reduce future remediation costs through the following practices:
- Add automated tests for critical state machines;
- Document typical bug fix processes into troubleshooting playbooks;
- Add necessary state logs and exception contexts directly in the code;
- Utilize AI coding assistants for code comprehension, impact surface analysis, and test supplementation, rather than solely for generating code.
For state transition logic in particular, it is highly recommended to lock down expected behavior through automated test cases. As business states grow more complex, the value of these tests increases significantly.
A successful bug fix does not necessarily require the developer to manually modify code line by line, but it absolutely requires the developer to accurately describe the problem, design reproduction paths, and verify the results. Tools can alleviate a massive amount of repetitive labor, but humans are still needed to define the standard of “correctness” and take responsibility for the outcome.
For development teams, integrating these practices into code maintenance, technical debt management, and regression verification can significantly lower troubleshooting costs. It also enables less experienced members to participate in resolving complex issues more rapidly. The ultimate goal is not to chase the fastest single-point fix, but to establish a repeatable, verifiable, and continuously optimizable engineering methodology.
