Problem: Compose Decorators Dynamically Based on Configuration

Hard
40 min
Build a decorator composition system that applies different decorators at runtime, depending on configuration flags.

Problem statement

Your platform operates in multiple environments: developmentstaging, and production. Each environment needs a different level of instrumentation for a critical async service:

  • development: Log inputs and outputs

  • staging: Log + time execution

  • production: Timing only

You’ll first build the two decorators—withLogging and withTiming—and then write a function that composes them dynamically based on a configuration object—without hardcoding or repeating decorator logic.

This pattern is common in large Node.js systems, where instrumentation and monitoring are applied selectively.

Goal

  1. Implement two reusable decorators:

    1. withLogging(fn): logs arguments and return values.

    2. withTiming(fn): measures execution time.

  2. Implement a decorate(fn, config) function that:

    1. Receives a function and a configuration object, e.g., { logging: true, timing: true }.

    2. Dynamically composes the relevant decorators.

    3. Returns a new decorated function that behaves according to config.

Constraints

  • Do not modify the base service function.

  • The decorator composition must happen inside decorate().

  • The order of application must be: withTiming(withLogging(fn)) when both are enabled.

  • You must not use if statements inside the decorators themselves.

Sample output

The examples below illustrate what the output should look like:

const env = 'staging'; // Try with different values: 'development', 'staging', 'production'
const config = {
development: { logging: true },
staging: { logging: true, timing: true },
production: { timing: true }
}[env];
const decoratedProcessOrder = decorate(processOrder, config);
(async () => {
await decoratedProcessOrder(123);
})();
/* Expected output:
with env = 'development':
[LOG] Called with args: [123]
[LOG] Returned: { id: 123, status: 'processed' }
with env = 'staging':
[LOG] Called with args: [123]
[LOG] Returned: { id: 123, status: 'processed' }
[TIMER] processOrder took 82ms
with env = 'production':
[TIMER] processOrder took 81ms */

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Problem: Compose Decorators Dynamically Based on Configuration

Hard
40 min
Build a decorator composition system that applies different decorators at runtime, depending on configuration flags.

Problem statement

Your platform operates in multiple environments: developmentstaging, and production. Each environment needs a different level of instrumentation for a critical async service:

  • development: Log inputs and outputs

  • staging: Log + time execution

  • production: Timing only

You’ll first build the two decorators—withLogging and withTiming—and then write a function that composes them dynamically based on a configuration object—without hardcoding or repeating decorator logic.

This pattern is common in large Node.js systems, where instrumentation and monitoring are applied selectively.

Goal

  1. Implement two reusable decorators:

    1. withLogging(fn): logs arguments and return values.

    2. withTiming(fn): measures execution time.

  2. Implement a decorate(fn, config) function that:

    1. Receives a function and a configuration object, e.g., { logging: true, timing: true }.

    2. Dynamically composes the relevant decorators.

    3. Returns a new decorated function that behaves according to config.

Constraints

  • Do not modify the base service function.

  • The decorator composition must happen inside decorate().

  • The order of application must be: withTiming(withLogging(fn)) when both are enabled.

  • You must not use if statements inside the decorators themselves.

Sample output

The examples below illustrate what the output should look like:

const env = 'staging'; // Try with different values: 'development', 'staging', 'production'
const config = {
development: { logging: true },
staging: { logging: true, timing: true },
production: { timing: true }
}[env];
const decoratedProcessOrder = decorate(processOrder, config);
(async () => {
await decoratedProcessOrder(123);
})();
/* Expected output:
with env = 'development':
[LOG] Called with args: [123]
[LOG] Returned: { id: 123, status: 'processed' }
with env = 'staging':
[LOG] Called with args: [123]
[LOG] Returned: { id: 123, status: 'processed' }
[TIMER] processOrder took 82ms
with env = 'production':
[TIMER] processOrder took 81ms */

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Node.js
// Mock async service
async function processOrder(orderId) {
return new Promise((resolve) => {
setTimeout(() => resolve({ id: orderId, status: 'processed' }), 80);
});
}
// Your code here
// Example usage
const env = 'staging'; // Try with different values: 'development', 'staging', 'production'
const config = {
development: { logging: true },
staging: { logging: true, timing: true },
production: { timing: true }
}[env];
const decoratedProcessOrder = decorate(processOrder, config);
(async () => {
await decoratedProcessOrder(123);
})();
/* Expected output:
with env = 'development':
[LOG] Called with args: [123]
[LOG] Returned: { id: 123, status: 'processed' }
with env = 'staging':
[LOG] Called with args: [123]
[LOG] Returned: { id: 123, status: 'processed' }
[TIMER] processOrder took 82ms
with env = 'production':
[TIMER] processOrder took 81ms */