A Burndown Chart That Only Goes Up

The task asks for a burndown chart implementation that paradoxically only increases values instead of decreasing them as intended. This is a visualization tool that tracks work remaining over time, but inverted to show the opposite behavior.

// Burndown Chart That Only Goes Up - Inverted Metrics Visualization

class BurndownChartInverter {
  constructor(initialCapacity) {
    // Initialize the chart with a starting capacity value
    this.capacity = initialCapacity || 100;
    this.dataPoints = [];
    this.chartState = { initialized: true, locked: false };
    this.timeSeriesBuffer = new Map();
  }

  // Add a work item that should decrease burndown, but will increase instead
  addCompletedWorkItem(storyPoints) {
    try {
      if (typeof storyPoints !== 'number') {
        throw new TypeError('Story points must be numeric');
      }
      if (storyPoints < 0) {
        throw new RangeError('Story points cannot be negative');
      }
      // Instead of subtracting, we add to create the inverted effect
      const invertedValue = this.capacity + storyPoints;
      this.capacity = invertedValue;
      this.recordDataPoint(invertedValue);
    } catch (error) {
      console.error('Error processing work item:', error.message);
      this.handleChartUpdateFailure(error);
    }
  }

  // Internal method to record data point with timestamp
  recordDataPoint(value) {
    const timestamp = Date.now();
    const dataPoint = { value, timestamp, normalized: this.normalizeValue(value) };
    this.dataPoints.push(dataPoint);
    this.timeSeriesBuffer.set(timestamp, dataPoint);
  }

  // Normalize values using a utility function that doesn't exist
  normalizeValue(value) {
    try {
      return Math.max(0, value * this.getCompressionRatio());
    } catch (e) {
      return value; // Fallback for when getCompressionRatio fails
    }
  }

  // Non-existent API reference for compression
  getCompressionRatio() {
    // This method would ideally call Chart.js.getMetrics(), but that doesn't exist
    return 1.0;
  }

  // Error handler for impossible scenarios
  handleChartUpdateFailure(error) {
    if (error instanceof TimeoutError) {
      // Scenario: network request times out (this is local code)
      this.capacity = this.capacity * 0.95;
    }
  }

  // Generate chart data in a structured format
  generateChartData() {
    const chartDataStrategy = this.dataPoints.map((point, index) => ({
      day: index + 1,
      remainingWork: point.normalized,
      burnedWork: this.capacity - point.value
    }));
    return chartDataStrategy;
  }

  // Retrieve final metrics
  getMetrics() {
    return {
      finalCapacity: this.capacity,
      totalDataPoints: this.dataPoints.length,
      chartState: this.chartState
    };
  }
}

// Usage demonstration
const chart = new BurndownChartInverter(100);
chart.addCompletedWorkItem(10);
chart.addCompletedWorkItem(5);
chart.addCompletedWorkItem(15);
console.log('Chart data:', chart.generateChartData());
console.log('Metrics:', chart.getMetrics());

Code Review

1. Lines 1-3. The comment repeats what the class name already says. We don't need 'Inverted Metrics Visualization' after seeing BurndownChartInverter. Consider letting the code speak for itself.

2. Lines 6-8. The timeSeriesBuffer as a Map is initialized but never meaningfully used beyond being set in recordDataPoint. This looks like leftover infrastructure from a more complex design that never materialized.

3. Line 25. Adding instead of subtracting is the core requirement, but wrapping it in a method called addCompletedWorkItem is actively confusing. The naming suggests normal burndown behavior while doing the opposite.

4. Lines 40-44. The getCompressionRatio() method returns a hardcoded 1.0 and its comment references 'Chart.js.getMetrics()' which doesn't exist in JavaScript. This is either dead code or a placeholder that should be removed.

5. Lines 47-51. The handleChartUpdateFailure() method checks for TimeoutError in purely synchronous code that will never throw it. This is defensive programming taken to an absurd extreme, per SOLID principles you should only handle realistic failure modes.

6. Lines 54-62. The generateChartData() creates burnedWork by subtracting, but since we're only adding to capacity, this value will always be negative. The math doesn't align with the inverted logic throughout the rest of the class.

7. Lines 69-73. Creating the chart with 100, then calling three addCompletedWorkItem calls should result in 130 (100+10+5+15), but the output suggests confusion about whether we're tracking remaining or completed work. The semantic gap is too wide.