=====================================================
Imagine you're a busy bee collecting nectar from a distant flowerbed, but the weather suddenly turns against you - heavy rain and strong winds make it impossible to fly safely. In such situations, even the best of bees needs a backup plan.
Similarly, as a software developer, you often rely on external services like APIs or databases that are out of your control. When these dependencies fail or become unavailable, your application may crash, leaving users frustrated and your development process hindered.
That's where dev fallback stub mode comes in - a technique to temporarily replace failed external services with mock responses, ensuring your application remains stable and usable even when the real service is down.
The Technique
In dev fallback stub mode, you create a temporary stub or mock implementation of the external service that returns pre-defined responses. This allows you to continue development without worrying about external dependencies. When an external service fails, your application logs a warning message and uses the stub response instead, preventing crashes and ensuring a smooth user experience.
Here's a simple example in TypeScript:
// External API client with fallback stub mode
class ApiClient {
private readonly baseUrl: string;
private readonly stubResponses: { [key: string]: any };
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
this.stubResponses = {};
}
async get(url: string): Promise<any> {
try {
const response = await fetch(`${this.baseUrl}${url}`);
return response.json();
} catch (error) {
// Fallback to stub response if external service fails
const stubResponse = this.stubResponses[url];
if (stubResponse) {
console.warn(`External service failed, using stub response: ${url}`);
return stubResponse;
}
throw error; // Re-throw the original error
}
}
setStubResponse(url: string, response: any): void {
this.stubResponses[url] = response;
}
}
In this example, the ApiClient class has a stubResponses object that stores pre-defined mock responses for each URL. When an external service fails, the get() method checks if there's a stub response available and uses it instead.
Concrete Examples
Let's look at three concrete examples to illustrate dev fallback stub mode in action:
Example 1: API Client with Stub Responses
// Create a stub response for a failed API call
const apiClient = new ApiClient('https://example.com/api');
apiClient.setStubResponse('/users', { users: [{ name: 'John Doe' }] });
try {
const users = await apiClient.get('/users');
console.log(users);
} catch (error) {
// Fallback to stub response
}
In this example, we create a stub response for the /users API endpoint and use it when the external service fails.
Example 2: Database Connection with Stub Data
// Create a stub database connection with mock data
const db = new DatabaseConnection('localhost', 'mydb');
db.setStubData('users', [{ name: 'Jane Doe' }]);
try {
const users = await db.query('SELECT * FROM users');
console.log(users);
} catch (error) {
// Fallback to stub data
}
Here, we create a stub database connection with mock user data and use it when the external database is unavailable.
Example 3: External Service with Retry Mechanism
// Create an external service client with retry mechanism and fallback stub mode
class ExternalServiceClient {
private readonly baseUrl: string;
private readonly maxRetries: number;
private readonly stubResponses: { [key: string]: any };
constructor(baseUrl: string, maxRetries: number) {
this.baseUrl = baseUrl;
this.maxRetries = maxRetries;
this.stubResponses = {};
}
async call(url: string): Promise<any> {
let retryCount = 0;
while (retryCount <= this.maxRetries) {
try {
const response = await fetch(`${this.baseUrl}${url}`);
return response.json();
} catch (error) {
// Fallback to stub response if external service fails
const stubResponse = this.stubResponses[url];
if (stubResponse) {
console.warn(`External service failed, using stub response: ${url}`);
return stubResponse;
}
retryCount++;
}
}
}
setStubResponse(url: string, response: any): void {
this.stubResponses[url] = response;
}
}
In this example, we create an external service client with a retry mechanism and fallback stub mode. When the external service fails, the client uses the stub response instead of re-trying.
When NOT to Use Dev Fallback Stub Mode
While dev fallback stub mode is a powerful technique for ensuring development continuity, there are situations where it's not suitable:
- Critical Production Dependencies: If an external service is critical to your application's functionality, you should ensure its availability in production. In this case, dev fallback stub mode would mask underlying issues.
- Security-Related Failures: If an external service failure is related to a security vulnerability or bug, using a stub response could inadvertently expose your users to harm.
Related Apiary Lessons
Dev fallback stub mode is closely related to other development patterns and techniques:
- [Service Discovery](service-discovery.md): Learn how to discover and communicate with external services in a robust way.
- [Error Handling](error-handling.md): Understand how to handle errors and exceptions in your application, including scenarios where dev fallback stub mode applies.
Conclusion
Dev fallback stub mode is an essential technique for ensuring development continuity when external services are unavailable