Introduction

Welcome back to Applying What You've Learned! You have now completed two lessons: setting up project configurations and adding features with automatic skill selection. In both lessons, everything worked smoothly on the first attempt. However, real development rarely proceeds without mistakes, and that is perfectly fine. In fact, making mistakes and recovering from them is an essential part of learning and building software.

In this third lesson, we will explore how to recover from errors when building with Claude Code. We will intentionally make a mistake while adding validation to our Todo API, recognize the problem through test failures, and then work with Claude to correct the implementation. This practice will build your confidence to experiment freely, knowing that you can always fix issues when they arise.

Understanding Conversation State

Before we practice error recovery, let us understand an important concept: Claude Code maintains a complete history of your conversation, including every file change, command execution, and tool use. You can see your current session information using the /status command, which shows details like your session ID, current working directory, and configuration settings.

When you are working with Claude and need to undo changes, you have several options. You can ask Claude to revert specific changes, use version control tools like git, or restart from a known good state. The key is that Claude can help you recover from mistakes by creating new corrected versions of files or by helping you navigate your version control history.

Setting Up the Validation Task

We are going to add validation for the priority field in our Todo API. Currently, todos can have a priority of low, medium, or high, with medium as the default. However, there is no validation preventing invalid priority values from reaching our database. Let us add a validation middleware to solve this:

This request is clear and focused. We want to validate the priority field before it reaches the route handler. Validation middleware is a common pattern in Express applications, sitting between the route and the handler to check input data.

Claude begins by reading the existing validation file to understand the current structure.

Examining Current Validators

Claude first inspects what validators already exist:

The middleware file src/middleware/validate.js contains two validators: one for general todo input (validateTodoInput) and one for email addresses (validateEmail). Our new priority validator will follow the same pattern as these existing functions. This consistency is important because it makes the codebase easier to understand and maintain.

Now Claude is ready to add the new validator, but here is where we will intentionally introduce a subtle mistake that will help us learn about error recovery.

Adding the Validation Middleware (With a Mistake)

Claude adds the validatePriority validation function to the middleware file:

This middleware contains a critical mistake: it requires the priority field to be present. The first check rejects any request where priority is missing. However, based on our requirements from the previous lesson, priority should be optional and default to medium when not specified. This validation is too strict and will break existing functionality.

The mistake is subtle because the code looks reasonable at first glance. The validation logic itself is correct (checking for valid values), but the requirement check (making it mandatory) contradicts our feature specification.

Applying the Middleware to Routes

After creating the validator, Claude integrates it into the todo routes:

The middleware is now active in the request pipeline. Every request to create a todo must pass through validatePriority before reaching the handler. This is standard Express middleware composition, and the code follows our conventions perfectly.

At this point, we might feel satisfied: we added a validator and applied it correctly. But let us see what happens when we run the tests.

Discovering the Problem

Claude runs the test suite to verify the changes:

We have a test failure. Three tests pass, but one fails: the test that verifies defaulting to medium priority when the field is not specified. The error message is telling: Priority is required. This is the exact error our validator returns when priority is missing from the request body.

This is the moment where we recognize something is wrong. The test was passing before, and now it fails after our changes. The failed test reveals the mistake: our validator incorrectly requires priority to be present, when it should actually be optional.

Recovering from the Mistake

Now comes the important part: recovering from the mistake. We can simply ask Claude to fix the problem:

This direct request tells Claude exactly what went wrong and what we need. Claude understands that the validation logic needs to change to make priority optional while still validating it when provided.

Implementing the Correct Validation

Claude updates the validation to make priority optional:

The corrected version has one crucial change: we removed the check that rejects missing priorities. Now the validator only runs if priority is actually present in the request body (the priority && condition). If priority is missing, the middleware calls next() immediately, allowing the request to proceed. The default value of medium will be applied later in the route handler or model, as designed.

This is the proper implementation. The validator ensures that when priority is provided, it must be one of the valid values. When priority is absent, the validator does not interfere.

The routes file does not need to change because the issue was in the validator logic, not in how it was applied. The middleware integration remains the same.

Running Tests Successfully

Now for the moment of truth. Let us run the tests again with the corrected validation:

All tests pass. The second test, which failed before, now passes because our validator correctly allows requests without a priority field. The fourth test still passes because the validator correctly rejects invalid priority values when they are provided. The validation works exactly as intended.

This success demonstrates the complete error recovery cycle: recognize the problem through test failures, clearly communicate what needs to change, let Claude implement the fix, and verify success with tests.

Understanding the Recovery Process

Let us reflect on what we just practiced:

Each step in this recovery process is important. Making mistakes is inevitable; what matters is having automated tests to catch them and a clear communication path with Claude to fix them. The tests provide immediate feedback about what is wrong, and clear instructions to Claude enable quick corrections.

The final point is crucial: this workflow enables confident experimentation. When you know that tests will catch mistakes and Claude can help fix them, you feel more comfortable trying new approaches and testing ideas. Mistakes become learning opportunities rather than setbacks.

Connecting to the Full Learning Path

We have now completed three lessons that build upon each other. Let us see how everything connects:

This summary shows how the three units work together to form a complete workflow. In Unit 1, we configured our project with CLAUDE.md, .claudeignore, and permissions. In Unit 2, we added features and watched skills like api-tester activate automatically. In Unit 3, we learned to recover from mistakes during iteration using tests and clear communication.

The workflow is cyclical: we configure our environment, create reusable skills, apply them to real tasks, and iterate with confidence knowing we can catch and fix errors. Each component supports the others, creating a robust development process.

Conclusion and Next Steps

In this lesson, we practiced error recovery by intentionally making a mistake while adding validation, recognizing it through test failures, and then working with Claude to implement the correct solution. This experience demonstrates that mistakes are not permanent setbacks but rather temporary detours in the development process that automated testing helps us catch early.

The combination of automatic testing, clear communication with Claude, and quick iteration creates an environment where we can experiment freely. When we know that tests will catch mistakes and Claude can help fix them quickly, we feel more comfortable trying new approaches and learning from failures. This psychological safety is just as important as the technical capabilities.

You have now completed the core learning path for Claude Code: project configuration, skill utilization, and error recovery. These three pillars form the foundation of effective development with Claude. Now it is time to take what you have learned and apply it through hands-on practice, where you will tackle real challenges and build your confidence in this powerful workflow!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal