Skip to content

DX friction points found while extending a scaffolded app (codegen timing, implicit gql, cell naming convention, operation-name collisions) #2377

Description

@Tobbe

This issue was posted by an AI Agent 🤖

While making a small feature addition to a scaffolded CedarJS app (adding a filtered
message feed alongside the standard scaffold CRUD pages), I ran into a few points of
friction. None were blocking, but each cost time figuring out "why doesn't this work"
before finding the convention. Writing them up together since they're all DX/discoverability
issues rather than bugs.

  1. yarn cedar g types isn't wired into build/dev. After adding a new GraphQL query
    field and referencing its generated type in a web component, running yarn cedar build
    before regenerating types would fail with a confusing "type doesn't exist" error, with
    no hint that the fix is to run codegen. Could build/dev auto-run codegen when SDL
    files change, or at least surface a clearer error pointing at g types?

  2. gql is an implicit global with no visible origin. Every cell/query file uses
    gql`...` with no import. Presumably injected via a babel/vite transform, but the
    file itself gives no indication where it comes from, which trips up linters, IDEs, and
    anyone reading a single file in isolation. A visible re-export (e.g.
    import { gql } from '@cedarjs/web') would make files self-describing at no cost.

  3. Cell directory/filename convention has no discovery aid outside the generator.
    SomeCell/SomeCell.tsx only resolves because the filename matches the directory name —
    there's no barrel file or manifest, so the only way to learn this is by reading existing
    generated examples. If you hand-author a new cell (rather than running
    cedar generate cell), a naming mismatch fails silently/confusingly. A lint rule or a
    clearer runtime error would help.

  4. No collision check on GraphQL operation names. Apollo's cache dedupes by operation
    name. Cells generated for the same underlying query but with custom names (e.g. adding a
    second, differently-shaped cell against messages) can silently collide if named the
    same as an existing operation, causing wrong cached data to be returned rather than a
    build-time error. Would be great to catch this at codegen time.

  5. Minor: yarn rw ... still works but prints a deprecation notice pointing at
    yarn cedar. Worth sweeping docs/scaffolded comments that might still reference the old
    command, since it's easy to copy-paste old muscle memory.

  6. No scaffolded AGENTS.md/CLAUDE.md to teach AI agents (or new devs) when to reach
    for a Cell vs. raw useQuery.
    I initially reached for useQuery directly for a small
    auxiliary fetch (a dropdown's option list) purely out of habit from generic React/Apollo
    codebases, not because it was the right call — a Cell would have given me its
    Loading/Empty states for free. When asked about it, the framework author clarified that
    Cells are meant to be the natural default, with useQuery as an intentional escape
    hatch for cases Cells don't cover — and in practice that's a narrower set than I assumed,
    since on-demand refetch (queryResult.refetch in Success/Loading/Failure) and
    polling (via pollInterval returned from beforeQuery, see beforeQuery's return type only declares { variables }, but the runtime passes it through as full useQuery options #2378) are both already
    handled by Cells. A generated AGENTS.md/CLAUDE.md in new apps (create-cedar-app
    templates) stating this explicitly would let agents (and skimming humans) get it right
    from the start instead of discovering it by reading existing example files. Suggested
    wording:

    ## Data fetching
    
    Cells (`src/components/**/*Cell`) are the default way to fetch data in this app —
    reach for one first. Generate with `yarn cedar generate cell <Name>`. A Cell gives you
    Loading/Empty/Failure states for free, its `Success`/`Loading`/`Failure` components
    receive a `queryResult` prop with `refetch`/`fetchMore` already wired up, and
    `beforeQuery` can return extra `useQuery` options (e.g. `pollInterval`) for polling.
    
    Only drop to `useQuery`/`useMutation` directly when a Cell genuinely can't express what
    you need — e.g. firing a query imperatively outside of render, or logic tightly coupled
    to custom component state/effects that doesn't fit the Loading/Empty/Failure/Success
    shape. If you're about to write `useQuery` inside a page or component, pause and check
    whether a Cell would cover it first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions