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
- Performance analysis and optimization: Isolate load balancing / Stream backpressure / Memory optimization
- AOT compilation (
dart compile exe) and output slimming - CI/CD configuration: GitHub Actions +
dart test+dart analyze - pub.dev publishing workflow and documentation (README / CHANGELOG / API Docs)
- Alice's final acceptance: million-order end-to-end stress test report
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.
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"]
> **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
- Processing a million orders dropped from 45 seconds to 8 seconds
- Memory reduced from 2GB to 180MB
- Build output shrank from 80MB to 15MB
- CI/CD automation guarantees quality with every commit
3. Performance Analysis & Optimization
(1) Isolate Load Balancing
▶ Example
> **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
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});
}
> **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
> **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
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;
}
}
}
> **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
> **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
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),
);
}
> **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
> **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
# 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
> **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
> **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
// 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');
}
}
}
> **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
> **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
# .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
> **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
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
> **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
# 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
> **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
# 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
> **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
> **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
/// 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
}
}
> **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
// ============================================
// 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);
}
}
> **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
stripto remove debug symbols andtree shakingto remove unused code. A typical Dart CLI AOT build is 10-20MB.
Q: Is the Isolate creation overhead significant? A:
Isolate.spawntakes ~50-100ms,Isolate.runtakes ~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 analyzehas 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/cacheto cache~/.pub-cache, with the key based on the hash ofpubspec.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.csvdirectly, checking the exit code and output. This is closer to the production environment thandart run.
📖 Summary
- Performance optimization trio: Dynamic Isolate sharding, Stream backpressure control, chunked processing for memory limits
- AOT compilation gives Dart CLI native-level startup speed;
strip+tree shakingslim down the output - CI/CD automation: Format → Analyze → Test → Compile → Integration Test → Publish
- Publishing to pub.dev requires a complete pubspec.yaml, README, CHANGELOG, and API documentation
- Alice accepted: 8 seconds for a million orders, 180MB memory, 15MB build output
📝 Exercises
- Basic (Difficulty ⭐): Add a Makefile/script for
dart compile exeto DataPipeline, enabling one-click compilation of the release build and output of the file size. - 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.
- 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!