Dart: Project Optimization & Deployment — DataPipeline

Last updated: 2026-08-26

Code that runs isn't enough; code that runs fast, stable, and safely is an engineer's delivery. — Charlie

1. What You Will Learn


2. A Developer's Real Story

(1) Pain Point: Runs but Runs Slowly

DataPipeline processed 10K orders in just 0.3 seconds. When Alice applied it to a million-order dataset for stress testing, it took 45 seconds. Bob identified the issues: uneven Isolate sharding (only 1 of 4 cores was working), no backpressure in Streams causing memory to spike to 2GB, and an 80MB product compiled in debug mode.

(2) The Optimized Solution

Dynamic Isolate sharding balanced the load across 4 cores, adding backpressure to Streams kept memory under 200MB, AOT compilation reduced the output to 15MB, and CI/CD automated testing and deployment.

100%
flowchart LR
  A[Code Complete] --> B[Performance Optimization]
  B --> C[Isolate Tuning]
  B --> D[Stream Backpressure]
  B --> E[Memory Analysis]
  C --> F[AOT Compilation]
  F --> G[CI/CD]
  G --> H[GitHub Actions]
  H --> I[pub.dev Publishing]
  I --> J["v1.0.0"]
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

(3) Benefits


3. Performance Analysis & Optimization

(1) Isolate Load Balancing

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Dynamic vs. Static Sharding

DART
import 'dart:isolate';

// Bad: Static chunking - uneven work
List<List<T>> staticChunk<T>(List<T> data, int chunks) {
  final size = (data.length / chunks).ceil();
  return [
    for (var i = 0; i < chunks; i++)
      data.sublist(i * size, ((i + 1) * size).clamp(0, data.length))
  ];
}

// Good: Dynamic work stealing with SendPort
class DynamicIsolatePool {
  final int _poolSize;
  var _pendingChunks = <List<Map<String, dynamic>>>[];
  final _results = <AnalysisResult>[];
  var _activeWorkers = 0;
  late Completer<List<AnalysisResult>> _completer;

  DynamicIsolatePool({int poolSize = 4}) : _poolSize = poolSize;

  Future<List<AnalysisResult>> process(
    List<Map<String, dynamic>> allOrders, {
    int chunkSize = 50000,
  }) async {
    _pendingChunks = [
      for (var i = 0; i < allOrders.length; i += chunkSize)
        allOrders.sublist(i, (i + chunkSize).clamp(0, allOrders.length))
    ];

    _completer = Completer<List<AnalysisResult>>();
    _activeWorkers = 0;

    final workers = <_Worker>[];
    for (var i = 0; i < _poolSize && _pendingChunks.isNotEmpty; i++) {
      final chunk = _pendingChunks.removeAt(0);
      workers.add(await _spawnWorker(chunk));
      _activeWorkers++;
    }

    return _completer.future;
  }

  Future<_Worker> _spawnWorker(List<Map<String, dynamic>> initialChunk) async {
    final receivePort = ReceivePort();
    final isolate = await Isolate.spawn(_workerEntry, receivePort.sendPort);
    final sendPort = await receivePort.first as SendPort;

    final responsePort = ReceivePort();
    sendPort.send(_WorkMessage(data: initialChunk, responsePort: responsePort.sendPort));

    responsePort.listen((result) {
      _results.add(result as AnalysisResult);
      _activeWorkers--;

      if (_pendingChunks.isNotEmpty) {
        final nextChunk = _pendingChunks.removeAt(0);
        sendPort.send(_WorkMessage(data: nextChunk, responsePort: responsePort.sendPort));
        _activeWorkers++;
      } else if (_activeWorkers == 0) {
        isolate.kill(priority: Isolate.immediate);
        receivePort.close();
        responsePort.close();
        _completer.complete(_results);
      }
    });

    return _Worker(isolate: isolate, sendPort: sendPort);
  }

  static void _workerEntry(SendPort mainSendPort) {
    final receivePort = ReceivePort();
    mainSendPort.send(receivePort.sendPort);

    receivePort.listen((message) {
      if (message is _WorkMessage) {
        final orders = message.data.map((m) => Order(
          id: m['id'] as String,
          amount: (m['amount'] as num).toDouble(),
          status: m['status'] as String,
          category: m['category'] as String,
        )).toList();

        final result = const OrderAnalyzer().analyze(orders);
        message.responsePort.send(result);
      }
    });
  }
}

class _Worker {
  final Isolate isolate;
  final SendPort sendPort;
  const _Worker({required this.isolate, required this.sendPort});
}

class _WorkMessage {
  final List<Map<String, dynamic>> data;
  final SendPort responsePort;
  const _WorkMessage({required this.data, required this.responsePort});
}
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.
Optimization Area Before After Improvement
Sharding Strategy Static equal split Dynamic work stealing 30% faster
Chunk Size 1 chunk/worker 50K items/chunk Memory halved
Worker Count Fixed 4 Adaptive to CPU cores Faster on multi-core

(2) Stream Backpressure

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Backpressure Control

DART
import 'dart:async';

class BackpressurePipeline {
  final DataSource _source;
  final OrderParser _parser;
  final int _maxConcurrent;

  BackpressurePipeline({
    required DataSource source,
    required OrderParser parser,
    int maxConcurrent = 1000,
  })  : _source = source,
        _parser = parser,
        _maxConcurrent = maxConcurrent;

  Future<AnalysisResult> execute() async {
    final orders = <Order>[];
    var pending = 0;
    final completer = Completer<void>();

    final subscription = _source.readLines().listen(
      (line) {
        try {
          orders.add(_parser.parse(line));
        } on FormatException {
          return;
        }

        pending++;
        if (pending >= _maxConcurrent) {
          subscription.pause();
        }
      },
      onDone: () {
        completer.complete();
      },
      onError: (e) {
        completer.completeError(e);
      },
    );

    await completer.future;

    return const OrderAnalyzer().analyze(orders);
  }

  // Pausable stream with buffer control
  Stream<Order> streamWithBackpressure() async* {
    var buffer = <Order>[];

    await for (final line in _source.readLines()) {
      try {
        buffer.add(_parser.parse(line));
      } on FormatException {
        continue;
      }

      if (buffer.length >= _maxConcurrent) {
        for (final order in buffer) {
          yield order;
        }
        buffer = [];
      }
    }

    for (final order in buffer) {
      yield order;
    }
  }
}
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.
Backpressure Strategy Description Use Case
pause/resume Pauses the source stream Consumer processes slowly
Buffer limit Fixed-size buffer Controllable memory usage
drop latest Discard data exceeding limit Tolerable loss in real-time monitoring

(3) Memory Optimization

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Memory Analysis Techniques

DART
import 'dart:math';

class MemoryOptimizer {
  // Use iterables instead of lists where possible
  static Iterable<Order> lazyParse(Iterable<String> lines, OrderParser parser) sync* {
    for (final line in lines) {
      try {
        yield parser.parse(line);
      } on FormatException {
        continue;
      }
    }
  }

  // Chunk processing to limit memory
  static Future<AnalysisResult> processInChunks(
    Stream<String> lines,
    OrderParser parser, {
    int chunkSize = 100000,
  }) async {
    var chunk = <Order>[];
    var totalResult = _EmptyAnalysisResult();

    await for (final line in lines) {
      try {
        chunk.add(parser.parse(line));
      } on FormatException {
        continue;
      }

      if (chunk.length >= chunkSize) {
        final partial = const OrderAnalyzer().analyze(chunk);
        totalResult = totalResult.merge(partial);
        chunk = [];
      }
    }

    if (chunk.isNotEmpty) {
      final partial = const OrderAnalyzer().analyze(chunk);
      totalResult = totalResult.merge(partial);
    }

    return totalResult.toAnalysisResult();
  }
}

class _EmptyAnalysisResult {
  int totalOrders = 0;
  int completedOrders = 0;
  double revenue = 0;
  double tax = 0;
  final Map<String, double> revenueByCategory = {};

  _EmptyAnalysisResult merge(AnalysisResult other) {
    totalOrders += other.totalOrders;
    completedOrders += other.completedOrders;
    revenue += other.revenue;
    tax += other.tax;
    for (final entry in other.revenueByCategory.entries) {
      revenueByCategory.update(entry.key, (v) => v + entry.value, ifAbsent: () => entry.value);
    }
    return this;
  }

  AnalysisResult toAnalysisResult() => AnalysisResult(
    totalOrders: totalOrders,
    completedOrders: completedOrders,
    revenue: revenue,
    tax: tax,
    total: revenue + tax,
    revenueByCategory: Map.unmodifiable(revenueByCategory),
  );
}
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.
Optimization Technique Effect Use Case
Lazy Iterable No pre-loading of all data Passing data item-by-item from source to consumer
Chunk Processing Constant memory usage Processing millions of records
Result Merging Small intermediate results Aggregating statistics after chunking

4. AOT Compilation & Output Slimming

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Compilation Commands & Optimization

BASH
# Debug build (large, includes debug info)
dart compile exe bin/data_pipeline.dart -o build/data_pipeline_debug

# Release build (optimized, smaller)
dart compile exe bin/data_pipeline.dart -o build/data_pipeline \
  --define=MODE=release

# Check binary size
ls -lh build/

# Strip debug symbols (additional size reduction)
# On Linux/macOS:
strip build/data_pipeline
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.
Compilation Mode Command Output Size Startup Speed
JIT (dart run) dart run bin/main.dart 0 (runs source) Slow
AOT exe dart compile exe ~15MB Fast
AOT aot-snapshot dart compile aot-snapshot ~8MB Fastest (requires dartaotruntime)

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Compile-Time Configuration

DART
// Conditional imports for tree shaking
const _isRelease = bool.fromEnvironment('MODE.release');

void log(String message) {
  if (!_isRelease) {
    print('[DEBUG] $message');
  }
}

// This entire class will be tree-shaken in release mode
class DebugLogger {
  void log(String message) {
    if (!_isRelease) {
      print('[DEBUG] ${DateTime.now()}: $message');
    }
  }
}
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

5. CI/CD — GitHub Actions

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Complete CI Configuration

YAML
# .github/workflows/ci.yml
name: DataPipeline CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  analyze-and-test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        sdk: [stable, beta]

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1
        with:
          sdk: ${{ matrix.sdk }}

      - name: Install dependencies
        run: dart pub get

      - name: Verify formatting
        run: dart format --output=none --set-exit-if-changed .

      - name: Analyze code
        run: dart analyze --fatal-infos

      - name: Run tests
        run: dart test --coverage=coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: coverage/lcov.info

  build-and-release:
    needs: analyze-and-test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1

      - name: Install dependencies
        run: dart pub get

      - name: Compile AOT binary
        run: |
          dart compile exe bin/data_pipeline.dart -o build/data_pipeline

      - name: Run integration test
        run: |
          ./build/data_pipeline analyze --source csv --input test/fixtures/sample.csv

      - name: Upload binary artifact
        uses: actions/upload-artifact@v4
        with:
          name: data_pipeline-linux
          path: build/data_pipeline

  publish:
    needs: build-and-release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1

      - name: Install dependencies
        run: dart pub get

      - name: Publish to pub.dev
        run: dart pub publish --force
        env:
          PUB_CREDENTIALS: ${{ secrets.PUB_CREDENTIALS }}
CI Stage Checks Performed Action on Failure
Formatting dart format Auto-format PR
Static Analysis dart analyze Block merge
Unit Tests dart test Block merge
Integration Test AOT binary end-to-end Block publish
Publishing dart pub publish Triggered only by tag

6. Publishing to pub.dev

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: pubspec.yaml Publishing Configuration

YAML
name: data_pipeline
description: E-commerce data analytics CLI tool - process million-level orders with Stream + Isolate
version: 1.0.0

homepage: https://github.com/bob/datapipeline
repository: https://github.com/bob/datapipeline
issue_tracker: https://github.com/bob/datapipeline/issues

environment:
  sdk: ^3.0.0

dependencies:
  args: ^2.4.2
  http: ^1.1.0
  csv: ^6.0.0

dev_dependencies:
  test: ^1.24.0
  build_runner: ^2.4.0
  json_serializable: ^6.7.0

executables:
  data_pipeline: data_pipeline

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: CHANGELOG.md

MARKDOWN
# CHANGELOG


---

## 7. Release Notes

### (1) 1.0.0 - 2025-01-15

#### Features
- CSV and JSON data source support
- Stream-based pipeline with backpressure
- Isolate pool for parallel processing
- CLI with analyze/export/validate subcommands
- Pattern Matching + Sealed class architecture

### (2) Performance
- 1M orders processed in ~8 seconds (4-core)
- Memory usage under 200MB for 1M records

### (3) Testing
- 95% code coverage
- Unit + integration + E2E tests

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Pre-Publish Checklist

BASH
# Pre-publish checklist

# 1. Dry run publish
dart pub publish --dry-run

# 2. Check score on pub.dev
dart pub publish --dry-run 2>&1 | grep -E "score|warning|error"

# 3. Generate API docs
dart doc .

# 4. Verify package contents
dart pub pack  # creates .tar.gz for inspection

# 5. Final publish
dart pub publish
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.
Publishing Step Command Purpose
Dry Run dart pub publish --dry-run Check package contents
Generate Docs dart doc . API documentation
Version Check pubspec.yaml version Semantic versioning
Publish dart pub publish Push to pub.dev

7. API Documentation Generation

▶ Example

TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

: Documentation Comment Standards

DART
/// Analyzes e-commerce order data and produces statistical results.
///
/// Use [OrderAnalyzer] as the primary entry point for data analysis.
/// Configure the [taxRate] based on the target region.
///
/// Example:
/// ```dart
/// final analyzer = OrderAnalyzer(taxRate: 0.08);
/// final result = analyzer.analyze(orders);
/// print('Revenue: \$${result.revenue} USD');
/// ```
///
/// See also:
/// - [AnalysisResult] for the output structure
/// - [Order] for the input model
class OrderAnalyzer {
  /// Creates an analyzer with the given [taxRate].
  ///
  /// Defaults to 0.08 (8% US tax rate).
  /// Use region-specific rates: US=0.08, EU=0.20, UK=0.15.
  const OrderAnalyzer({this.taxRate = 0.08});

  /// The tax rate applied to completed orders.
  final double taxRate;

  /// Analyzes a list of [orders] and returns aggregated statistics.
  ///
  /// Only orders with status 'completed' are included in revenue
  /// calculations. Orders with other statuses are counted in
  /// [AnalysisResult.totalOrders] but excluded from revenue.
  AnalysisResult analyze(List<Order> orders) {
    // ... implementation
  }
}
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

8. Complete Example: Performance Benchmark

DART
// ============================================
// DataPipeline Performance Benchmark
// Alice's End-to-End Stress Test
// ============================================

import 'dart:async';
import 'dart:io';

class Benchmark {
  final String name;
  final Stopwatch _stopwatch;

  Benchmark(this.name) : _stopwatch = Stopwatch();

  Future<T> run<T>(Future<T> Function() action) async {
    _stopwatch.reset();
    _stopwatch.start();
    final result = await action();
    _stopwatch.stop();
    print('$name: ${_stopwatch.elapsedMilliseconds}ms');
    return result;
  }
}

// Generate test data
List<String> generateOrders(int count) {
  final categories = ['Electronics', 'Clothing', 'Books', 'Home', 'Sports'];
  final statuses = ['completed', 'completed', 'completed', 'pending', 'cancelled'];
  final regions = ['US', 'EU', 'UK', 'JP'];

  return List.generate(count, (i) {
    final category = categories[i % categories.length];
    final status = statuses[i % statuses.length];
    final region = regions[i % regions.length];
    final amount = (100 + (i * 37) % 5000).toDouble();
    return 'ORD-${(i + 1).toString().padLeft(7, '0')},$amount,$status,$category,$region';
  });
}

void main() async {
  print('=== DataPipeline Performance Benchmark ===\n');

  final sizes = [10000, 100000, 1000000];

  for (final size in sizes) {
    final label = size >= 1000000 ? '${size ~/ 1000000}M' : '${size ~/ 1000}K';
    print('--- $label orders ---');

    // Generate and save to temp file
    final data = generateOrders(size);
    final tempFile = File('${Directory.systemTemp.path}/bench_${label}.csv');
    await tempFile.writeAsString(data.join('\n'));

    // Benchmark 1: Stream processing
    final streamResult = await Benchmark('Stream ($label)').run(() async {
      final source = CsvSource(path: tempFile.path);
      final parser = const CsvOrderParser();
      final pipeline = Pipeline(source: source, parser: parser);
      return pipeline.execute();
    });

    // Benchmark 2: Memory-efficient chunked processing
    final chunkedResult = await Benchmark('Chunked ($label)').run(() async {
      final source = CsvSource(path: tempFile.path);
      return MemoryOptimizer.processInChunks(
        source.readLines(),
        const CsvOrderParser(),
        chunkSize: 50000,
      );
    });

    // Verify results match
    final streamRev = streamResult.revenue.toStringAsFixed(2);
    final chunkedRev = chunkedResult.revenue.toStringAsFixed(2);
    print('Revenue match: ${streamRev == chunkedRev ? "PASS" : "FAIL"}');
    print('Revenue: \$${streamRev} USD\n');

    await tempFile.delete();
  }

  // Benchmark 3: AOT binary size check
  print('--- Build Artifacts ---');
  final exe = File('build/data_pipeline');
  if (await exe.exists()) {
    final sizeKB = await exe.length() ~/ 1024;
    print('AOT binary: ${sizeKB}KB');
  } else {
    print('AOT binary: not built (run: dart compile exe bin/data_pipeline.dart)');
  }

  print('\n=== Benchmark Complete ===');
  print('Alice acceptance criteria: 1M orders < 10s, memory < 300MB, binary < 20MB');
}

// ---- Required stubs (imported from previous lessons) ----

sealed class DataSource {
  const DataSource();
  String get displayName;
  Stream<String> readLines();
}

class CsvSource extends DataSource {
  final String path;
  const CsvSource({required this.path});
  @override
  String get displayName => 'CSV: $path';
  @override
  Stream<String> readLines() =>
    File(path).openRead().transform(utf8.decoder).transform(const LineSplitter());
}

abstract class OrderParser {
  const OrderParser();
  Order parse(String raw);
}

class CsvOrderParser extends OrderParser {
  const CsvOrderParser();
  @override
  Order parse(String raw) {
    final parts = raw.split(',');
    if (parts.length < 4) throw FormatException('Invalid row');
    return Order(
      id: parts[0].trim(),
      amount: double.parse(parts[1].trim()),
      status: parts[2].trim(),
      category: parts[3].trim(),
      region: parts.length > 4 ? parts[4].trim() : 'US',
    );
  }
}

class Order {
  final String id;
  final double amount;
  final String status;
  final String category;
  final String region;
  const Order({required this.id, required this.amount, required this.status, required this.category, this.region = 'US'});
  double get tax => amount * 0.08;
}

class AnalysisResult {
  final int totalOrders;
  final int completedOrders;
  final double revenue;
  final double tax;
  final double total;
  final Map<String, double> revenueByCategory;
  const AnalysisResult({required this.totalOrders, required this.completedOrders, required this.revenue, required this.tax, required this.total, required this.revenueByCategory});
}

class OrderAnalyzer {
  const OrderAnalyzer();
  AnalysisResult analyze(List<Order> orders) {
    final completed = orders.where((o) => o.status == 'completed').toList();
    final revenue = completed.fold<double>(0, (s, o) => s + o.amount);
    final tax = completed.fold<double>(0, (s, o) => s + o.tax);
    final byCategory = <String, double>{};
    for (final o in completed) {
      byCategory.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
    }
    return AnalysisResult(totalOrders: orders.length, completedOrders: completed.length, revenue: revenue, tax: tax, total: revenue + tax, revenueByCategory: byCategory);
  }
}

class Pipeline {
  final DataSource source;
  final OrderParser parser;
  const Pipeline({required this.source, required this.parser});
  Future<AnalysisResult> execute() async {
    final orders = <Order>[];
    await for (final line in source.readLines()) {
      try { orders.add(parser.parse(line)); } on FormatException { continue; }
    }
    return const OrderAnalyzer().analyze(orders);
  }
}

class MemoryOptimizer {
  static Future<AnalysisResult> processInChunks(Stream<String> lines, OrderParser parser, {int chunkSize = 50000}) async {
    var chunk = <Order>[];
    int totalOrders = 0, completedOrders = 0;
    double revenue = 0, tax = 0;
    final byCategory = <String, double>{};

    await for (final line in lines) {
      try { chunk.add(parser.parse(line)); } on FormatException { continue; }
      if (chunk.length >= chunkSize) {
        final r = const OrderAnalyzer().analyze(chunk);
        totalOrders += r.totalOrders;
        completedOrders += r.completedOrders;
        revenue += r.revenue;
        tax += r.tax;
        for (final e in r.revenueByCategory.entries) {
          byCategory.update(e.key, (v) => v + e.value, ifAbsent: () => e.value);
        }
        chunk = [];
      }
    }
    if (chunk.isNotEmpty) {
      final r = const OrderAnalyzer().analyze(chunk);
      totalOrders += r.totalOrders;
      completedOrders += r.completedOrders;
      revenue += r.revenue;
      tax += r.tax;
      for (final e in r.revenueByCategory.entries) {
        byCategory.update(e.key, (v) => v + e.value, ifAbsent: () => e.value);
      }
    }
    return AnalysisResult(totalOrders: totalOrders, completedOrders: completedOrders, revenue: revenue, tax: tax, total: revenue + tax, revenueByCategory: byCategory);
  }
}
TEXT 📖 Display only
> **Output:** Execute locally in DartPad or with `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly with SDK versions.

9. Alice's Acceptance Report

Acceptance Criteria Target Actual Status
1M Order Processing Time < 10s ~8s PASS
Peak Memory Usage < 300MB ~180MB PASS
AOT Output Size < 20MB ~15MB PASS
Test Coverage > 90% 95% PASS
dart analyze 0 warnings 0 PASS
dart format 100% 100% PASS
Documentation Coverage 100% of public API 100% PASS
pub.dev score > 120 140 PASS

Alice: "DataPipeline v1.0 accepted! A million orders processed in 8 seconds, memory kept under 200MB, AOT build output is compact, and CI/CD automation is complete. Ready for release!"


❓ FAQ

Q: Why is the AOT build output so large? A: AOT output includes a streamlined Dart VM + compiled native code. Use strip to remove debug symbols and tree shaking to remove unused code. A typical Dart CLI AOT build is 10-20MB.

Q: Is the Isolate creation overhead significant? A: Isolate.spawn takes ~50-100ms, Isolate.run takes ~30ms. For long-running tasks, use an Isolate Pool to reuse workers and avoid frequent creation/destruction.

Q: How do Stream backpressure and Isolates work together? A: The main Isolate reads the Stream and buffers it to a fixed size, pausing when full. Worker Isolates consume the buffered data and notify the main Isolate to resume when they're done. It's similar to a producer-consumer pattern.

Q: How can I improve my pub.dev score? A: Ensure: 1) pubspec.yaml is complete (homepage/repository/description); 2) README.md is detailed; 3) dart analyze has zero warnings; 4) Test coverage > 80%; 5) Documentation comments cover all public APIs.

Q: How do I cache dependencies in GitHub Actions? A: Use actions/cache to cache ~/.pub-cache, with the key based on the hash of pubspec.lock. First run takes ~30s, cache hit takes ~3s.

Q: How do I test the AOT-compiled binary in CI? A: After dart compile exe, run ./build/data_pipeline analyze --input test.csv directly, checking the exit code and output. This is closer to the production environment than dart run.


📖 Summary


📝 Exercises

  1. Basic (Difficulty ⭐): Add a Makefile/script for dart compile exe to DataPipeline, enabling one-click compilation of the release build and output of the file size.
  2. Intermediate (Difficulty ⭐⭐): Implement the pause/resume mechanism for Stream backpressure. Verify with 10K order data processing: memory does not increase during pause, and processing resumes correctly.
  3. Challenge (Difficulty ⭐⭐⭐): Configure a complete GitHub Actions CI pipeline: formatting check + static analysis + unit test + AOT compilation + integration test. Push to GitHub to verify the pipeline passes.

← Previous Lesson | Course Complete — Congratulations on finishing all 24 lessons!

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏