This episode dissects how Dart works behind the scenes: the runtime with the JIT and AOT model, the library and package-based program structure, the type system with sound typing, and the Future and Stream foundations for asynchronous programming.

To become proficient in Dart, you need to understand what happens behind the scenes: how your code is executed, how programs are structured, and how the type system protects you from bugs. Episode 2 dissects Dart's language architecture from the runtime to the type system.
We'll cover the JIT and AOT compilation models, the library and package-based program structure, the concept of sound typing along with null safety, and the Future and Stream foundations. This material is the lens that will make all the following episodes make sense.
Don't worry if some terms feel abstract. Focus on understanding the concepts, while the syntax details you'll master in episodes 4 through 7.
During development, Dart code runs on the Dart VM using JIT (Just-In-Time). JIT compiles code while the application is running, enabling features like hot reload and hot restart in Flutter, as well as dart run which executes scripts directly. The instant startup makes the development loop feel light.
When the application is ready for release, Dart can be compiled AOT (Ahead-Of-Time) into native machine code. AOT eliminates the compilation cost at runtime, so startup is faster, memory is more stable, and performance is more predictable. The command used:
dart compile exe bin/app.dart -o bin/appThe result is a native executable that doesn't require the Dart SDK to run. The command dart compile exe bin/app.dart produces a binary file that can be run directly on the target machine without a separate runtime.
For web, Dart is compiled to JavaScript by dart compile js. Meanwhile, for Flutter applications, Dart code is compiled to machine code and run inside the Flutter engine, which is written in C++. One language, many targets: native, browser, and mobile.
All Dart code lives in a library. Every Dart file is an implicit library, and you can combine libraries with import to reuse code from other files or packages. The lib/ directory is where importable code lives, while bin/ is for entry points.
There's also a part mechanism for splitting one library into several files — but modern practice recommends import over part. Here's an example folder structure from a project created with dart create:
my_app/
├── bin/my_app.dart
├── lib/my_app.dart
├── test/my_app_test.dart
├── pubspec.yaml
└── analysis_options.yamlThis structure is already set up by dart create my_app and we'll explore it further in episodes 3 and 8.
Above the library, there's the package — a distribution unit that has a pubspec.yaml and can be published to pub.dev. A package combines code, metadata, dependencies, and tests. This concept is similar to a crate in Rust or an npm package in JavaScript.
By understanding this hierarchy — file, library, then package — you can determine where code should be placed and how it's imported by other code.
Dart is a language with sound typing: a variable's type is guaranteed by the compiler, and type errors are detected before the program runs. Combined with sound null safety, the compiler distinguishes nullable and non-nullable types, so null-pointer errors that are common in other languages can be prevented at compile time.
A short example of the type system:
void main() {
String nama = 'Dart';
int versi = 3;
print('$nama versi $versi');
}In the code above, String nama states that nama will never be null. If you try to assign null, the compiler rejects it before the application runs — that's the essence of sound typing, which we'll fully dissect in episode 6.
Dart's types fall into three main groups: int, double, and bool for basic values, String for text, and collection types like List, Set, and Map. All these types obey the same null safety and sound typing rules, so the principles you learn apply consistently across your whole program.
Operations like reading files, making HTTP calls, or waiting for network input must not block the main thread. Dart solves this with two fundamental tools:
Both are consumed with async and await. An example of using Future:
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 1));
return 'data siap';
}
void main() async {
var hasil = await fetchData();
print(hasil);
}await fetchData() delays execution without blocking the thread, because the runtime suspends that function. The full details of async and Stream will be covered in episode 7.
Key takeaways:
dart compile exe produces a native binary without a runtime; dart compile js for web.pubspec.yaml.Future represents a single future value; Stream represents many values over time.In the next episode 3, we'll install and run Dart hands-on — SDK verification, the dart repl REPL, the dart run command, creating a project with dart create, and adding your first dependency to pubspec.yaml. Get your terminal ready to start executing.