Files
GearBox/.planning/research/PITFALLS.md

20 KiB

Pitfalls Research

Domain: Gear management, collection tracking, purchase planning (single-user web app) Researched: 2026-03-14 Confidence: HIGH (domain-specific patterns well-documented across gear community and inventory app space)

Critical Pitfalls

Pitfall 1: Unit Handling Treated as Display-Only

What goes wrong: Weight and price values are stored as bare numbers without unit metadata. The app assumes everything is grams or dollars, then breaks when users enter ounces, pounds, kilograms, or foreign currencies. Worse: calculations like "total setup weight" silently produce garbage when items have mixed units. A 200g tent and a 5lb sleeping bag get summed as 205.

Why it happens: In a single-user app it feels safe to skip unit handling -- "I'll just always use grams." But real product specs come in mixed units (manufacturers list in oz, g, kg, lb), and copy-pasting from product pages means mixed data creeps in immediately.

How to avoid: Store all weights in a canonical unit (grams) at write time. Accept input in any unit but convert on save. Store the original unit for display purposes but always compute on the canonical value. Build a simple conversion layer from day one -- it is 20 lines of code now vs. a data migration later.

Warning signs:

  • Weight field is a plain number input with no unit selector
  • No conversion logic exists anywhere in the codebase
  • Aggregation functions (total weight) do simple SUM() without unit awareness

Phase to address: Phase 1 (Data model / Core CRUD) -- unit handling must be in the schema from the start. Retrofitting requires migrating every existing item.


Pitfall 2: Rigid Category Hierarchy Instead of Flexible Tagging

What goes wrong: The app ships with a fixed category tree (Shelter > Tents > 1-Person Tents) that works for bikepacking but fails for sim racing gear, photography equipment, or any other hobby. Users cannot create categories, and items that span categories (a jacket that is both "clothing" and "rain gear") get awkwardly forced into one slot. The "generic enough for any hobby" goal from PROJECT.md dies on contact with a rigid hierarchy.

Why it happens: Hierarchical categories feel structured and "correct" during design. Flat tags feel messy. But hierarchies require knowing the domain upfront, and GearBox explicitly needs to support arbitrary hobbies.

How to avoid: Use a flat tag/label system as the primary organization mechanism. Users create their own tags ("bikepacking", "sleep-system", "cook-kit"). An item can have multiple tags. Optionally allow a single "category" field for broad grouping, but do not enforce hierarchy. Tags are the flexible axis; a single category field is the structured axis.

Warning signs:

  • Schema has a category_id foreign key to a categories table with parent_id
  • Seed data contains a pre-built category tree
  • Adding a new hobby requires modifying the database

Phase to address: Phase 1 (Data model) -- this is a schema-level decision. Changing from hierarchy to tags after data exists requires migration of every item's categorization.


Pitfall 3: Planning Thread State Machine Complexity Explosion

What goes wrong: Thread items have statuses (researching, ordered, arrived) plus a thread-level resolution (pick winner, close thread, move to collection). Developers build these as independent fields without modeling the valid state transitions, leading to impossible states: an item marked "arrived" in a thread that was "cancelled," or a "winner" that was never "ordered." The UI then needs defensive checks everywhere, and bugs appear as ghost items in the collection.

Why it happens: Status tracking looks simple -- it is just a string field. But the combination of item-level status + thread-level lifecycle + the "move winner to collection" side effect creates a state machine with many transitions, and without explicit modeling, invalid states are reachable.

How to avoid: Model the thread lifecycle as an explicit state machine with defined transitions. Document which item statuses are valid in each thread state. The "resolve thread" action should be a single transaction that: (1) validates the winner exists, (2) creates the collection item, (3) marks the thread as resolved, (4) updates the thread item status. Use a state diagram during design, not just field definitions.

Warning signs:

  • Thread status and item status are independent string/enum fields with no transition validation
  • No transaction wrapping the "resolve thread + create collection item" flow
  • UI shows impossible combinations (resolved thread with "researching" items)

Phase to address: Phase 2 (Planning threads) -- design the state machine before writing any thread code. Do not add statuses incrementally.


Pitfall 4: Image Storage Strategy Causes Data Loss or Bloat

What goes wrong: Two failure modes: (A) Images stored as file paths break when files are moved, deleted, or the app directory changes. Dangling references show broken image icons everywhere. (B) Images stored as BLOBs in SQLite bloat the database, slow down backups, and make the DB file unwieldy as the collection grows.

Why it happens: Image storage seems like a simple problem. File paths are the obvious approach but create a coupling between database records and filesystem state. BLOBs seem self-contained but do not scale with photo-heavy collections.

How to avoid: Store images in a dedicated directory within the app's data folder (e.g., data/images/{item-id}/). Store relative paths in the database (never absolute). Generate deterministic filenames from item ID + timestamp to avoid collisions. On item deletion, clean up the image directory. For thumbnails under 100KB, SQLite BLOBs are actually 35% faster than filesystem reads, so consider storing thumbnails as BLOBs while keeping full-size images on disk.

Warning signs:

  • Absolute file paths in the database
  • No cleanup logic when items are deleted (orphaned images accumulate)
  • Database file growing much larger than expected (images stored as BLOBs)
  • No fallback/placeholder when an image file is missing

Phase to address: Phase 1 (Core CRUD with item photos) -- image handling must be decided before any photos are stored. Migrating image storage strategy later requires moving files and updating every record.


Pitfall 5: Setup Composition Breaks on Collection Changes

What goes wrong: A setup ("Summer Bikepacking") references items from the collection. When an item is deleted from the collection, updated, or replaced via a planning thread resolution, the setup silently breaks -- showing stale data, missing items, or incorrect totals. The user's carefully composed setup becomes untrustworthy.

Why it happens: Setups are modeled as a simple join table (setup_id, item_id) without considering what happens when the item side changes. The relationship is treated as static when it is actually dynamic.

How to avoid: Use foreign keys with explicit ON DELETE behavior (not CASCADE -- that silently removes setup entries). When an item is deleted, mark the setup-item link as "removed" and show a visual indicator in the setup view ("1 item no longer in collection"). When a planning thread resolves and replaces an item, offer to update setups that contained the old item. Setups should always recompute totals from live item data, never cache them.

Warning signs:

  • Setup totals are stored as columns rather than computed from item data
  • No foreign key constraints between setups and items
  • Deleting a collection item does not check if it belongs to any setup
  • No UI indication when a setup references a missing item

Phase to address: Phase 3 (Setups) -- but the foreign key design must be planned in Phase 1 when the items table is created. The item schema needs to anticipate setup references.


Pitfall 6: Comparison View That Does Not Actually Help Decisions

What goes wrong: The side-by-side comparison in planning threads shows raw data (weight: 450g, price: $120) without context. Users cannot see at a glance which candidate is lighter, cheaper, or how each compares to what they already own. The comparison becomes a formatted table, not a decision tool. Users go back to their spreadsheet because it was easier to add formulas.

Why it happens: Building a comparison view that displays data is easy. Building one that surfaces insights ("this is 30% lighter than your current tent but costs 2x more") requires computing deltas against the existing collection, which is a different feature than just showing two items side by side.

How to avoid: Design comparison views to show: (1) absolute values for each candidate, (2) deltas between candidates (highlighted: lighter/heavier, cheaper/more expensive), (3) delta against the current item being replaced from the collection. Use color coding or directional indicators (green down arrow for weight savings, red up arrow for cost increase). This is the core value proposition of GearBox -- do not ship a comparison that is worse than a spreadsheet.

Warning signs:

  • Comparison view is a static table with no computed differences
  • No way to link a thread to "the item I'm replacing" from the collection
  • Weight/cost impact on overall setup is not visible from the thread view

Phase to address: Phase 2 (Planning threads) -- comparison is the heart of the thread feature. Build the delta computation alongside the basic thread CRUD, not as a follow-up.


Technical Debt Patterns

Shortcuts that seem reasonable but create long-term problems.

Shortcut Immediate Benefit Long-term Cost When Acceptable
Caching setup totals in a column Faster reads, simpler queries Stale data when items change, bugs when totals disagree with item sum Never -- always compute from source items
Storing currency as float Simple to implement Floating point rounding errors in price totals (classic $0.01 bugs) Never -- use integer cents or a decimal type
Skipping "replaced by" links in threads Simpler thread resolution Cannot track upgrade history, cannot auto-update setups Only in earliest prototype, must add before thread resolution ships
Hardcoding unit labels Faster initial development Cannot support multiple hobbies with different unit conventions (e.g., ml for water bottles) MVP only if unit conversion layer is planned for next phase
Single image per item Simpler UI and storage Gear often needs multiple angles, especially for condition tracking Acceptable for v1 if schema supports multiple images (just limit UI to one)

Integration Gotchas

Common mistakes when connecting to external services.

Integration Common Mistake Correct Approach
Product link scraping Attempting to auto-fetch product details from URLs, which breaks constantly as sites change layouts Store the URL as a plain link. Do not scrape. Let users enter details manually. Scraping is a maintenance burden that exceeds its value for a single-user app.
Image URLs vs local storage Hotlinking product images from retailer sites, which break when products are delisted Always download and store images locally. External URLs rot within months.
Export/import formats Building a custom JSON format that only GearBox understands Support CSV import/export as the universal fallback. Users are migrating from spreadsheets -- CSV is their native format.

Performance Traps

Patterns that work at small scale but fail as usage grows.

Trap Symptoms Prevention When It Breaks
Loading all collection items for every setup view Slow page loads, high memory usage Paginate collection views; setup views should query only member items 500+ items in collection
Recomputing all setup totals on every item edit Edit latency increases linearly with number of setups Only recompute totals for setups containing the edited item 20+ setups referencing overlapping items
Storing full-resolution photos without thumbnails Page loads become unusably slow when browsing collection Generate thumbnails on upload; use thumbnails in list views, full images only in detail view 50+ items with photos
Loading all thread candidates for comparison Irrelevant for small threads, but threads can accumulate many "considered" items Limit comparison view to 3-4 selected candidates; archive dismissed ones 15+ candidates in a single thread

Security Mistakes

Domain-specific security issues beyond general web security.

Mistake Risk Prevention
No backup mechanism for SQLite database Single file corruption = total data loss of entire collection Implement automatic periodic backups (copy the .db file). Provide a manual "export all" button. Single-user apps have no server-side backup by default.
Product URLs stored without sanitization Stored URLs could contain javascript: protocol or XSS payloads if rendered as links Validate URLs on save (must be http/https). Render with rel="noopener noreferrer".
Image uploads without size/type validation Malicious or accidental upload of huge files or non-image files Validate file type (accept only jpg/png/webp) and enforce max size (e.g., 5MB) on upload.

UX Pitfalls

Common user experience mistakes in this domain.

Pitfall User Impact Better Approach
Requiring all fields to add an item Users abandon data entry because they do not know the weight or price yet for items they already own Only require name. Make weight, price, category, etc. optional. Users fill in details over time.
No bulk operations for collection management Adding 30 existing items one-by-one is painful enough that users never finish initial setup Provide CSV import for initial collection population. Consider a "quick add" mode with minimal fields.
Thread resolution is destructive User resolves a thread and loses all the research notes and rejected candidates Archive resolved threads, do not delete them. Users want to reference why they chose item X over Y months later.
Flat item list with no visual grouping Collection becomes an unscannable wall of text at 50+ items Group by tag/category in the default view. Provide sort options (weight, price, date added). Show item thumbnails in list view.
Weight displayed without context "450g" means nothing without knowing if that is heavy or light for this category Show weight relative to the lightest/heaviest item in the same category, or relative to the item being replaced
No "undo" for destructive actions Accidental deletion of an item with detailed notes is unrecoverable Soft-delete with a 30-day trash, or at minimum a confirmation dialog that names the item being deleted

"Looks Done But Isn't" Checklist

Things that appear complete but are missing critical pieces.

  • Item CRUD: Often missing image cleanup on delete -- verify orphaned images are removed when items are deleted
  • Planning threads: Often missing the "link to existing collection item being replaced" -- verify threads can reference what they are upgrading
  • Setup composition: Often missing recomputation on item changes -- verify that editing an item's weight updates all setups containing it
  • CSV import: Often missing unit detection/conversion -- verify that importing "5 oz" vs "142g" both result in correct canonical storage
  • Thread resolution: Often missing setup propagation -- verify that resolving a thread and adding the winner to collection offers to update setups that contained the replaced item
  • Comparison view: Often missing delta computation -- verify that the comparison shows differences between candidates, not just raw values side by side
  • Dashboard totals: Often missing staleness handling -- verify dashboard stats reflect current data, not cached snapshots
  • Item deletion: Often missing setup impact check -- verify the user is warned "This item is in 3 setups" before confirming deletion

Recovery Strategies

When pitfalls occur despite prevention, how to recover.

Pitfall Recovery Cost Recovery Steps
Mixed units without conversion MEDIUM Add unit column to items table. Write a migration script that prompts user to confirm/correct units for existing items. Recompute all setup totals.
Rigid category hierarchy HIGH Migrate categories to tags (each leaf category becomes a tag). Update all item references. Redesign category UI to tag-based UI.
Thread state machine bugs MEDIUM Audit all threads for impossible states. Write a cleanup script. Add transition validation. Retest all state transitions.
Image path breakage LOW-MEDIUM Write a script that scans DB for broken image paths. Move images to canonical location. Update paths. Add fallback placeholder.
Stale setup totals LOW Drop cached total columns. Replace with computed queries. One-time migration, no data loss.
Currency as float MEDIUM Multiply all price values by 100, change column type to integer (cents). Rounding during conversion may lose sub-cent precision.

Pitfall-to-Phase Mapping

How roadmap phases should address these pitfalls.

Pitfall Prevention Phase Verification
Unit handling Phase 1: Data model Schema stores canonical grams + original unit. Conversion utility exists with tests.
Category rigidity Phase 1: Data model Items have a tags array/join table. No hierarchical category table exists.
Image storage Phase 1: Core CRUD Images stored in data/images/ with relative paths. Thumbnails generated on upload. Cleanup on delete.
Currency precision Phase 1: Data model Price stored as integer cents. Display layer formats to dollars/euros.
Thread state machine Phase 2: Planning threads State transitions documented in code. Invalid transitions throw errors. Resolution is transactional.
Comparison usefulness Phase 2: Planning threads Comparison view shows deltas. Thread can link to "item being replaced." Setup impact visible.
Setup integrity Phase 3: Setups Totals computed from live data. Item deletion warns about setup membership. Soft-delete or archive for removed items.
Data loss / no backup Phase 1: Infrastructure Automatic DB backup on a schedule. Manual export button on dashboard.
Bulk import Phase 1: Core CRUD CSV import available from collection view. Handles unit variations in weight column.

Sources


Pitfalls research for: GearBox -- gear management and purchase planning app Researched: 2026-03-14