2. What tcm does away with
Storing each definition by the hash of its code, with names as labels on hashes, takes away a lot of what development usually involves. Each claim below says where it stops, and names the test that proves it: tcm's own end-to-end tests, run on every change to tcm (the links are by hash, so a test renamed keeps its link, and one removed can't be without this page saying so).
2.1 Code
- No dead code to eliminate. Code runs, and apps are bundled, from what they reach: definitions nothing reaches
are never written out or bundled, so there's nothing to shake out. Where it stops: npm packages are still
tree-shaken by the bundler, and a class is one definition, shipped whole. Proved by
testBundlesHoldOnlyWhatTheAppReaches. - No circular imports. Definitions that refer to each other are hashed and stored together, as one module, so
the modules code runs from never import each other round a loop: there's no import order to get wrong.
Where it stops: mutual recursion is fine; top-level constants that need each other to exist first fail as they
would in plain TypeScript. Proved by
testNoCircularImports. - Renames are free. A rename moves a label: no code is edited, nothing breaks, every use shows the new name.
Where it stops: a function's
.nameat runtime is part of its hash, so it keeps the name it was written with (hashing). Proved bytestInitAddMvUpdateTodoEditRunandtestClassesKeepIdentityThroughRenames. - No half-finished changes. An update carries through to everything that still typechecks, and puts the rest
on a todo list, keeping its old version. Proved by
testInitAddMvUpdateTodoEditRun. - No formatting churn. Formatting never changes a hash, so stored code is always formatted, and a restyle is
never a change to diff, merge or review. Proved by
testFmtRestylesWithNoHashChanging. - Docs that don't go stale. Links and examples in doc comments are stored by hash: they follow renames and
updates, and examples are run. Where it stops: links between pages are kept as written
(limitations). Proved by
testDocsFollowRenamesAndUpdates.
2.2 Packages and repositories
- No packages to share code. Code is shared by name, across namespaces, not by packaging it;
tcm packis only for publishing to npm. Where it stops: inside one codebase. Another codebase's code can't yet be used as a library without pulling its branch (limitations). - No monorepo. One codebase holds any number of apps and npm packages, as namespaces, even on different majors
of a dependency; packages depend on each other's releases with no workspace tooling. Where it stops: a package
can't be packed while it uses another's unreleased or internal code. Proved by
testGeometryExampleandtestPackagesDependThroughReleases. - No
package.jsonto maintain. Import a package; its version comes from the code around it, and tcm installs it and its types. Where it stops:tcm initwrites apackage.jsonfor Node's import map (#codebase,#tcm/*), generated, with no dependencies in it;tcm packwrites one for npm. Proved bytestPackagesPinnedByRange. - Two versions of a dependency, side by side. A definition records the range it was written against, so code on
one major and code on the next run together. Where it stops: one app can't mix ranges of a package others are
built against; transitive dependencies aren't pinned. Proved by
testPackTwoRangesSideBySideandtestPackagesOneRangePerApp. - Release versions and changelogs nobody writes. The version comes from semver checks of what changed in the
package's API, and the notes list those changes. Where it stops: types, not behaviour, are checked. Proved by
testSemverChecks.
2.3 Building, testing and linting
- Nothing to build while you work, and builds made once. Node runs the stored code directly; a production build
is named by the app's hash and reused, and builds in well under a second (the largest example's, userinput's, in
about 0.7). Where it stops: JSX, and some syntax of imported projects, is compiled as it loads; a new version's
production build bundles the whole app again (its unchanged chunks keep their URLs). Proved by
testBuildStartDeployRollBack. - Each definition tested and linted once. A test's result and a definition's lint findings are cached by hash:
only what a change reaches runs again, and end-to-end suites by the code they actually ran. Where it stops: the
unit is the definition, not the file; results that depend on the clock, the network, files or the environment can
go stale (limitations). Proved by
testTestsCachedByHash,testSuitesRunAgainOnlyWhenCodeTheyRanChangedandtestLintWithNothingToSetUp. - Shared outputs with no setup. Test results and lint findings travel with
tcm pushandtcm pull, so a clone, CI or another machine doesn't run or lint again what passed elsewhere: no remote cache to configure. Where it stops: production builds, watch values and kept constants are each machine's own (a kept constant's value is code, which isn't taken on trust); a pulled result is trusted as the remote's code is. Proved bytestResultsTravelandtestResultsTravelThroughAServer.
2.4 Apps
- No deciding where code runs, or organizing it by side. Where each definition runs, server or browser, is
inferred from what it uses, and browser code calls server functions by importing them: no routes, no fetch
calls, no
"use client"or"use server". Where it stops: an npm package's export that can't be followed (a computedimport()) falls back to a guess about the whole package, a call across must be to an async function, and only plain data crosses. Proved bytestDevPlacementAndCallsByHashandtestReactPlacementStreamingHydration. - Skew protection with no setup. Calls go by hash and earlier builds stay served, so an open tab keeps calling
the code it loaded; a job runs the code that queued it. Where it stops: a tab on a pruned build reloads (410),
as does one reading rows newer code wrote; builds are kept by one server, so several behind a load balancer each
keep their own. Proved by
testBuildStartDeployRollBack,testRuntimeReloadsRetiredBuildandtestBackgroundJobs. - Instant rollbacks. Rolling back is moving a name back: the earlier build is still there. Where it stops: a
build pruned away is built again first (in under a second). Proved by
testBuildStartDeployRollBack. - Deploys that re-download only what changed. Browser code ships in chunks named by their content and kept
across deploys, and a changed chunk arrives as the difference from the one the browser has. Where it stops:
browsers without compression dictionaries get the whole chunk. Proved by
testAssetsKeepTheirUrlsAcrossDeploysandtestDeployedChunksArriveAsDifferences. - Easy migrations. A table's row type is its schema; changing it adds a typed step, applied as rows are read,
and
tcm updatesays up front what the stored rows need. A new hash format moves a codebase withtcm migrate, history and all. Where it stops: keep a step while an older build still writes the old shape. Proved bytestMigrationsUniqueIndexesUpdatesandtestMigrateKeepsEverything.