This episode covers Dart tooling and build configuration: build_runner and code generation with source_gen, static analysis with dart analyze, formatting with dart format, and a CI/CD pipeline for Dart projects.

At this point, your project already has logic, tests, and maybe production. Now it's time to tidy up the technical side: tooling and build configuration. Episode 14 covers code generation with build_runner, static analysis, formatting, and automation through CI/CD.
Code generation removes tedious boilerplate, the analyzer keeps code quality in check, the formatter ensures consistent style, and CI/CD guarantees all of it runs on every change. All four are standards any professional Dart project should have.
Many Dart patterns — JSON serialization, immutable data, mocks — require repetitive code that's prone to errors. Tools like json_serializable and freezed generate this code from concise definitions. Build runner is the engine that runs those generators:
dart run build_runner builddart run build_runner build runs all registered generators and writes the results to .g.dart files. During development, dart run build_runner watch runs the generators automatically every time a file changes.
Add the json_serializable and build_runner dependencies, then define your model:
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable()
class User {
final String id;
final String nama;
User({required this.id, required this.nama});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}part 'user.g.dart' links the generator's output file. _$UserFromJson and _$UserToJson are generated automatically, eliminating error-prone manual serialization.
build_runner is built on source_gen — a framework for writing your own generators. Large teams use it to generate API clients, routers, or bindings from domain schemas. When repetitive patterns start to hurt, that's the sign it's time to write a generator.
The analyzer inspects code without running it, finding errors, warnings, and style issues:
dart analyzedart analyze shows a complete list of issues with file and line locations. Make it a habit to have zero issues before every commit.
Control the analyzer's rules in analysis_options.yaml:
include: package:lints/recommended.yaml
analyzer:
exclude:
- "**/*.g.dart"
linter:
rules:
- prefer_final_locals
- avoid_printinclude: package:lints/recommended.yaml loads the recommended built-in rules. Generated files are excluded because they aren't hand-written code.
Dart has an official, consistent formatter:
dart format .dart format . tidies indentation and line breaks across the entire project. The formatter isn't about taste — consistency keeps pull request diffs small and reviews faster.
In CI, verify that the code is formatted using --output=none mode:
dart format --output=none --set-exit-if-changed .dart format --output=none --set-exit-if-changed . fails if any file isn't formatted, making it a perfect quality gate in a CI pipeline.
A minimal pipeline runs format, analysis, and tests:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- run: dart pub get
- run: dart format --output=none --set-exit-if-changed .
- run: dart analyze
- run: dart testThe workflow above installs Dart via the official dart-lang/setup-dart action, then runs the format check, the analyzer, and all tests on every push and pull request. dart pub get is run with the lockfile for deterministic builds.
Once all gates pass, the pipeline can compile an AOT binary (dart compile exe), build a Docker image, and deploy. For Flutter, add flutter test and flutter build steps. Episode 19 will cover the operational runbooks that accompany this deployment.
Key takeaways:
dart run build_runner build generates code from generators like json_serializable.part and @JsonSerializable automate JSON serialization without boilerplate.dart analyze checks errors and style; keep the result at zero.analysis_options.yaml.dart format . keeps style consistent across the project.In the next episode 15, we'll cover performance optimization — AOT compilation and tree shaking, profiling with DevTools and allocation analysis, performance tips for Flutter and servers, and benchmarking and runtime analysis.