Ask
27

Previews stopped working entirely after splitting the app into modules — is that just the cost, or is there a way back?

We split a large app into feature modules a few months ago. Build times improved and the structure is much better.

What we lost is previews. They either fail to build, hang, or come back with an error about not being able to find something. In the modules that still work, they take long enough that nobody waits for them.

The practical effect is that everyone now builds and runs the whole app to look at one screen, which is exactly the loop modularising was supposed to shorten. Two people have said they miss the old monolith and I do not have a good answer.

Is this an inherent cost of modules, or are we doing something that makes it worse than it needs to be?

3 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @preview_needs_less · 6h ago

    The mental model that fixed this for us: a preview is a unit test with a screen. Everything true of a hard-to-test view is true of a hard-to-preview one, and the remedy is the same.

    Which means the view should take its data as a value, not fetch it. If a view constructs its own view model that opens a connection, the preview has to make that work, and it either fails or hangs. If the view takes already-loaded state as a parameter, the preview passes it a literal and there is nothing to fail.

    In practice that means splitting most screens into two: a dumb view that takes state and emits events, and a thin container that wires it to real data. Preview the dumb one. It renders instantly because it depends on nothing, and as a bonus you can preview all its states — empty, loading, error, long text — which you could never do against a live data source.

    That last point is worth more than the speed. Most of the value people get from previews is seeing the awkward states, and a preview wired to a real backend only ever shows you the happy path.

    This is the same split that makes the view testable, so it is not extra work invented for the tooling. If the previews are hard, the tests are hard too, and you probably have fewer of them than you would like.

    25
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @module_graph_mo · 5h ago

    Not inherent, and the cause is almost always the same: the module containing the view still depends on most of the app.

    A preview builds the view's module and everything that module depends on. If your feature module imports a shared module that imports networking, analytics, the database layer and a dozen others, the preview is compiling most of the app to render one screen. It is slow for the same reason the app is slow to build, and it is fragile because anything failing anywhere in that graph fails the preview.

    So the diagnosis is to look at what the module actually pulls in. Draw or dump the dependency graph for one feature module and you will usually find one or two edges doing all the damage — commonly a "core" or "shared" module that has become a dumping ground and now transitively depends on everything.

    The fixes, in order of value:

    Split the dumping-ground module. It is almost always several unrelated things that ended up together for convenience. This is the big win and it improves build times generally, not just previews.

    Depend on protocols, not implementations. The view module should need the interface of your data source, not the module that talks to the network. That single change can cut a preview's dependency graph by most of its size.

    Keep the view module free of anything with heavy initialisation. Analytics and database setup in a module's startup path are a common cause of previews that hang rather than fail.

    30
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @sample_data_sena · 5h ago

    The piece that makes the above practical: a small sample-data module that everything can depend on and that depends on nothing.

    One module, no dependencies, containing realistic instances of your model types — a few users, a few items, an empty case, a case with unreasonably long text, an error. Feature modules depend on it in previews and in tests.

    Why it is worth its own module rather than scattering fixtures around: it stops sample data from being defined next to the real data layer, which is how a preview ends up dragging the networking module in through the back door. It also means the sample data is shared, so everyone previews against the same awkward cases and somebody's long-name bug gets caught by someone else's screen.

    Two practical notes:

    Do not ship it. Keep it out of release builds so it does not add size, and so nobody accidentally uses a fixture in production code.

    Include the ugly cases deliberately. The value is in previewing the states that break layouts. Sample data that is all short, tidy strings gives you previews that always look fine and an app that does not.

    Once this exists, the fix in the previous answer becomes about ten minutes per screen instead of a refactor, which is what makes it actually happen.

    19
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report