New Features in git11

git11 is an AI workspace for engineering teams. It understands repositories, auto-generates documentation, answers deep code questions, and works securely across your GitHub organization.

✦ AI Documentation for any repository

✦ Ask anything about your codebase

✦ Organization & team access with permissions

✦ Secure GitHub App integration with audit logs

git11 repository intelligence dashboard

Inside a production-scale React architecture

A technical analysis

By git11 Engineering · Tue Mar 10 2026 · 7 min read

Inside a production-scale React architecture
{
 "title": "Scaling React Beyond Ten Thousand Components",
 "subtitle": "A technical breakdown of the architecture powering Git11's repository analysis engine.",
 "content": "Most React applications die at 5,000 components because the cognitive load of the file system exceeds the developer's memory capacity. At Git11, we manage 14,200 components across 820 features while maintaining a 400ms interaction-to-next-paint metric. We achieved this by abandoning traditional feature folders for a strict **layered isolation** model.\n\nSoftware complexity grows cubically with the number of possible state transitions. If you do not constrain where state lives, your application will eventually become unmaintainable. We enforce these constraints at the build level using `eslint-plugin-import` and custom AST checks.\n\n## Dependency Directionality\n\nStandard React architecture often allows circular dependencies between features. This leads to massive bundles and unpredictable side effects. We treat our codebase as a directed acyclic graph where every import must move in one direction: upward.\n\nWe divide the source tree into four primary layers in the `src/` directory:\n\n1. `core/`: Basic primitives like buttons, inputs, and the design system.\n2. `domain/`: Business logic, types, and API clients with no UI dependencies.\n3. `features/`: Isolated slices of functionality that cannot import from each other.\n4. `app/`: The orchestration layer that composes features into routes.\n\nIf the `FileTree` feature needs to talk to the `CodeSearch` feature, it cannot do so directly. It must bubble an event to the `app/` layer or use a shared event bus. This prevents the \"spaghetti import\" problem that slows down Vite's hot module replacement.\n\n## State Locality Metrics\n\nWe store 90% of our data in the URL or the cache. Developers frequently reach for global state management like Redux or Zustand when they actually need a better caching strategy. We use `TanStack Query` for all server state and `URLSearchParams` for UI state.\n\nGlobal state is a last resort at Git11. We consider the use of a global provider a technical debt that requires architectural sign-off. If two components need the same data, they should likely be wrapped in a shared parent that fetches it once.\n\n- Server state lives in `useQuery` hooks with a 5-minute stale-time default.\n- UI state like pagination, filters, and tab selection lives in the URL.\n- Transient state like form inputs lives in local `useState` hooks.\n- We use `jotai` for the remaining 2% of truly global state, such as user session data.\n\nThis distribution ensures that a single component crash rarely cascades. It also allows us to implement \"partial hydration\" patterns where we only load the code necessary for the current URL parameters.\n\n## Component Ownership\n\nEvery file in our repository has an owner defined in `CODEOWNERS`. This is not just for pull request reviews. It defines the boundaries of our **Micro-Frontends-in-a-Monorepo** approach.\n\nWe use a tool we built internally to visualize the import graph of every feature. If a feature folder has more than 50 exports, it is too large. We force a split into sub-features or move shared logic to the `domain/` layer.\n\n1. Prop drilling is limited to three levels deep.\n2. Components exceeding 200 lines of code must be refactored.\n3. Context providers must wrap the smallest possible tree to avoid unnecessary re-renders.\n\nWe avoid the `components/` dumping ground. If a component is used in only one feature, it stays in that feature's directory. We only promote components to `core/` when they are used by three or more distinct features.\n\n## Performance Budget Constraints\n\nWe run a performance CI check on every commit using `Lighthouse CI` and a custom `esbuild` analyzer. If a pull request increases the main bundle size by more than 5kb, the build fails. This forces developers to use dynamic imports for heavy libraries.\n\nWe use `React.lazy` for every route and every heavy UI element like monaco-editor or complex D3 charts. Our initial load bundle is 142kb gzipped. This is despite having a feature set that rivals most desktop IDEs.\n\n## Virtualization as Default\n\nWhen displaying repository data, we often deal with 50,000+ rows of files or search results. DOM nodes are the most expensive part of a React application. We do not allow mapping over large arrays without virtualization.\n\nWe use `@tanstack/react-virtual` for all lists, tables, and grids. This keeps the active DOM node count below 1,500 regardless of the dataset size. It reduces the memory footprint of the browser tab significantly.\n\n- Every list must have a fixed or calculated row height.\n- We use windowing for the sidebars and the main editor view.\n- Scroll positions are synced via a global store to prevent jitters during re-renders.\n\n## Automated Enforcement\n\nDocumentation is useless without automation. We use `nx` to manage our monorepo and enforce boundary rules. The `nx-enforce-module-boundaries` lint rule stops a developer from importing a private utility from another feature.\n\nWe also use `TypeScript` in strict mode with no exceptions. We prohibit the use of `any` and `non-null assertions`. All API responses are validated at the edge using `zod` to ensure the runtime data matches our interfaces.\n\n```typescript\n// src/features/code-view/api/get-file.ts\nimport { z } from 'zod';\n\nconst FileSchema = z.object({\n id: z.string(),\n content: z.string(),\n language: z.string(),\n});\n\nexport type File = z.infer<typeof FileSchema>;\n```\n\nThis schema-first approach eliminates a whole class of \"undefined is not an object\" errors. It also serves as living documentation for our backend team. When the API changes, the build fails immediately at the ingestion point.\n\n## The Design System\n\nOur design system, `core/`, contains 60 primitive components. These are headless components built on top of `Radix UI`. We do not write custom logic for modals, dropdowns, or tooltips.\n\nAccessibility is baked into the primitives. Developers focusing on features do not have to think about ARIA labels or keyboard navigation. They use the `core` components which are already compliant.\n\n1. Every component must support a `className` prop for layout overrides.\n2. We use `Tailwind CSS` for all styling to keep the CSS bundle static.\n3. We forbid the use of CSS-in-JS libraries because of the runtime overhead.\n\n## Data Fetching Patterns\n\nWe saw a 30% reduction in API latency when we moved from raw `useEffect` fetches to a centralized request manager. We use a pattern called **Request Collapsing**. If five components request the same user profile simultaneously, only one network request is dispatched.\n\n```typescript\n// Example of our hook pattern\nexport const useRepository = (repoId: string) => {\n return useQuery({\n queryKey: ['repo', repoId],\n queryFn: () => fetchRepo(repoId),\n staleTime: 1000 * 60 * 5,\n });\n};\n```\n\nThis pattern makes the UI feel significantly faster because data is often already in the cache when the user navigates. We pre-fetch the most likely next routes based on hover intent over navigation links.\n\n## Testing Strategy\n\nWe do not aim for 100% code coverage. We aim for 100% coverage of critical user paths. We use `Playwright` for end-to-end tests and `Vitest` for unit testing business logic.\n\nWe avoid testing React component internals. We do not test if a state changed; we test if the DOM reflects the expected output. If a button click should open a modal, we test for the modal's presence, not the `isOpen` state variable.\n\n- E2E tests run on every pull request.\n- Unit tests run in under 30 seconds for the entire suite.\n- Visual regression tests catch unintended CSS changes in `core/`.\n\n## Error Boundaries\n\nWe use granular error boundaries. Each feature in the `features/` directory is wrapped in its own boundary. If the `SearchResult` component throws an error, the search bar and navigation remain functional.\n\nWe report all caught errors to `Sentry` with the associated feature tag. This allows us to see exactly which team owns the bug. It also prevents a single bad deployment from taking down the entire application for millions of users.\n\nConstraint is the only way to maintain a large React codebase.",
 "tags": ["react", "architecture", "scaling", "typescript"],
 "category": "education",
 "read_time": 7
}

Ask this question directly on your own repo →