Postcrossly - Linting, Documentation, and Staying Sharp
Three days after kicking off Postcrossly, today’s work shifted from “make it run” to “set it up right.” The app is still tiny: one domain model, a repository, a list screen, and a thin layer of tests. That’s exactly why I paused feature work. Early code hardens fast. If I don’t put a small quality scaffold in now, I’ll pay an unnecessary mental tax later.
Since the last post I introduced a simple Postcard
model (UUID-based id), a PostcardsRepository
with some initial basic operations, a first UI surface (PostcardsListScreen
), and a clearer entry point (PostcrosslyApp
). Along the way I configured linting, documented the public surface, and added integration‑flavored tests around repository behavior. None of this is “big” work, but together it shapes how the rest will feel to maintain.
Why Invest in Lint and Docs This Early
Early code feels obvious while it’s fresh. A week from now, the intent behind the code will already be less clear; a month or even a year later it will be even harder to recover. Lint rules slow me down just enough to encode that intent while it still lives in short‑term memory. They become automated micro‑reviews—especially useful when you’re solo and there is no second pair of eyes. Tests then turn today’s assumptions into a behavioral contract. And practicing this discipline on a side project keeps professional reflexes sharp; it’s easy to let standards slip “because it’s just a hobby app.” I’d rather build the habit of treating small code like production code.
The Lint Rules That Pull Their Weight
I didn’t aim for maximal strictness or pedantry. I turned on specific rules that defend clarity, reduce future guesswork, and keep diffs clean:
– Documentation hygiene: public_member_api_docs
forces me to narrate intent of public APIs; comment_references
nudges correct symbol references so docs don’t silently rot during renames.
– Type clarity: always_declare_return_types
and type_annotate_public_apis
eliminate “implicit” surprises and make signatures scan-friendly. Reading code later becomes faster because nothing relies on type inference for external contracts.
– Readability and diff quality: prefer_final_locals
discourages accidental mutation; require_trailing_commas
produces stable, minimal diffs and better formatter output; directives_ordering
keeps imports predictable so merge noise stays low.
– Safety and correctness: avoid_dynamic_calls
blocks accidental dynamic fallbacks; avoid_positional_boolean_parameters
keeps calls self‑describing (no mysterious true, false
sequences).
– Consistent style: prefer_single_quotes
and prefer_const_constructors
are tiny, but they smooth visual noise and help the formatter produce uniform output.
Each rule adds a nudge, not a burden. The aggregate effect is lower cognitive reload when I return after a break.
Documentation Style in Practice
I keep doc comments short and intention‑focused: a one‑line summary, optional expansion only when behavior is non‑obvious, and no parroting of what the type system already says. That keeps comments from becoming a second, diverging specification. Here’s the current repository (trimmed to the essentials):
/// Repository class for managing postcards
class PostcardsRepository {
final List<Postcard> _postcards = [];
/// Adds a postcard and returns it.
Postcard addPostcard(Postcard postcard) {
_postcards.add(postcard);
return postcard;
}
/// Retrieves by ID or returns null.
Postcard? getPostcard(String id) {
try {
return _postcards.singleWhere((p) => p.id == id);
} catch (_) {
return null;
}
}
/// Returns all postcards.
List<Postcard> fetchPostcards() => _postcards;
}
Short, semantic, and intention‑reinforcing. If I later swap the in‑memory list for persistence, these docs still hold and the tests (next section) make sure behavior doesn’t drift.
Early Tests (Style Agnostic, Value Focused)
Whether you prefer pure TDD or write code and add tests afterward, the key goal is to protect core behavior and user experience as early as possible. I focused on the repository first: it’s the seam where future persistence, caching, or sync logic will live. Even though today it’s just an in‑memory list, tests lock in expectations around the repository functionality. Those tests help ensure the code logic and user experience don’t break as I refactor or introduce persistence, and regressions are catched quickly.
Could I also test the screen now? Yes. It’s late today, so I limited myself to manual interaction to verify the basic UI wire‑up. In a real project that wouldn’t be acceptable: UI screens would not be released without proper widget and integration tests to validate behavior and protect the user experience. Here I’m allowing myself some slack because this is a personal side project and not shipping to customers, but I’ll pick this up first thing in the next session and add widget tests for the list rendering and interactions.
How This Pays Off Soon
When persistence or networking arrives the repository tests will let me refactor with confidence. The lint rules keep accidental dynamic looseness or silent API drift from slipping through. Lean doc comments act as quick memory triggers when I come back after a gap. All of this compounds: each small guard reduces friction on the next feature.
Tiny projects are a low‑risk arena to rehearse production discipline. The earlier the scaffolding, the cheaper every future change becomes. Side projects aren’t an excuse to lower standards—they’re a sandbox to practice them deliberately.
Hope you find this useful—see you in the next post.
You can follow the Postcrossly code as it evolves here: https://github.com/nuno-barreiro/postcrossly