Package & Plugin Development: What Senior Developers Know About Authoring
1. Why Interviewers Ask About Authoring
You will hit this question the moment your team decides to share code across two apps, or when your monorepo grows large enough to require strict domain boundaries. Almost every non-trivial Flutter team eventually needs to extract internal packages.
Most developers use the output of package development constantly—running flutter pub add without a second thought. But an engineer who has only ever consumed packages and never reasoned about what happens on the authoring side is missing a testable piece of senior fluency. Extracting a shared core_network package out of an existing app forces you to make decisions about platform boundaries, semantic versioning, and API visibility that you simply don't have to make when writing a standard application feature.
2. Dart Package vs. Flutter Plugin
This is the first concept that trips candidates up. If an interviewer asks you to extract your core_network logic, they want to know if you understand where the platform boundary sits.
A Dart Package is pure Dart. Even if your core_network package uses Flutter widgets to show network error dialogs, or depends on http, it is still just a package because it does not contain its own native code.
A Flutter Plugin is required when you need to write platform-specific code bridging to native APIs. If your core_network package needed to implement highly custom SSL certificate pinning using raw Kotlin on Android and Swift on iOS, it would cross the boundary and become a plugin. Getting this distinction right proves to the interviewer that you don't treat all reusable code as a single undifferentiated black box.
3. Pubspec Semantics for Authors
As a package consumer, you just paste things into dependencies. As a package author, placing a library in the wrong section has massive downstream consequences.
- dependencies: Required at runtime. If
core_networkdepends ondiohere, anyone who installs your package also downloadsdio. - dev_dependencies: Needed only for developing or testing the package itself (e.g.,
mocktailorflutter_lints). These are explicitly not shipped to consumers. - dependency_overrides: A local escape hatch to force a specific version or local file path during development. You must never publish a package with active overrides—it overrides the resolution for the consumer's entire project, causing dependency hell.
name: core_network
description: A pure Dart package for shared network logic.
version: 1.2.3
dependencies:
dio: ^5.3.0 # Shipped to consumers
dev_dependencies:
test: ^1.24.0 # Ignored by consumers
# dependency_overrides:
# dio:
# path: ../local_dio_fork <-- never="" publish="" span="" this="">-->
The Danger of Caret Syntax
When you define a version as ^1.2.3, you are telling the Dart pub resolver to use Semantic Versioning (Semver) rules.
Caret syntax allows any version that is considered "compatible"—meaning it will auto-upgrade minor and patch versions, but stop before the next major version. If an author incorrectly uses loose constraints like any or omits the caret, they risk silently pulling a breaking major change into every consumer's app on their next build. If they use a hardcoded version (e.g., 1.2.3 without the caret), they cause unnecessary version conflicts for the consumer.
4. Publishing, Semver, and Melos
Publishing a public package involves the dart pub publish command. But the technical command is easy; the discipline is what interviewers test. That discipline relies entirely on strict adherence to Semantic Versioning.
You changed an existing API signature or deleted a class.
You added a new endpoint method, fully backward-compatible.
You fixed an internal parsing crash without changing the API.
Every version bump requires a corresponding entry in your CHANGELOG.md. Consumers rely entirely on the changelog to determine if upgrading is safe. A package published without a human-readable changelog is considered unmaintained and dangerous.
If core_network lives inside a large monorepo alongside 15 other packages, bumping versions by hand is a nightmare. This is where you mention Melos to your interviewer: using commands like melos version and melos publish automates coordinated version bumps. If core_network has a breaking change, Melos ensures that every internal package depending on it also gets a version bump automatically.
5. Internal Registries: Where Code Lives
A common mistake candidates make is assuming "publishing a package" always means putting it on the public pub.dev registry. A proprietary core_network package containing your company's specific API authentication headers should absolutely not be public.
Mentioning private pub servers or path-dependencies shows the interviewer you understand enterprise security and codebase architecture, rather than just open-source hobby projects.
6. The Discipline of Minimal APIs
Extracting a package is meant to enforce architectural boundaries. If you extract core_network but accidentally expose all your internal JSON parsing utility classes to the consumer, you haven't created a boundary at all—you've just moved the spaghetti code to a new folder.
Dart packages enforce this via the lib/ directory and `export` statements. Files inside lib/src/ are considered private to the package. You create a single "barrel file" at lib/core_network.dart that explicitly curates the public API.
// BAD: Exposing implementation details to the consumer
export 'src/network_client.dart';
export 'src/internal_retry_logic.dart';
export 'src/raw_json_parser.dart';
// GOOD: Curated public API
export 'src/network_client.dart' show NetworkClient;
export 'src/models/network_error.dart';
// internal_retry_logic.dart remains safely hidden inside lib/src/
Q: When would you NOT publish an internal package to public pub.dev?
A: When it contains proprietary business logic, hardcoded internal API keys, or is highly coupled to the specific architectural quirks of our internal apps. We would use a private pub server or a monorepo path dependency instead.
7. Senior-Level Sample Answer
If an interviewer asks: "We have a lot of duplicated API request logic across our consumer app and our driver app. How would you handle sharing this code?"
core_network. I’d ensure it remains a standard package, not a plugin, by keeping any necessary native dependencies abstracted rather than writing custom platform code ourselves. I'd keep the public API minimal by exposing only a single barrel file and hiding implementation details inside lib/src/. Since this is proprietary logic, I wouldn't publish it to the public pub.dev registry; instead, we'd reference it as a path dependency if we migrate to a monorepo, or host it on a private pub server. Finally, I'd enforce strict Semantic Versioning—if we ever change the signature of our core API client, we bump the major version and update the changelog so the driver app team doesn't pull in a breaking change silently."
Before moving to the next post, answer this unscripted: If you fix a crash in your package without changing any class names or method signatures, which number in v1.4.2 do you increment before publishing?

Comments
Post a Comment