Start: Getting started: Install archangel, write a first table, see it find a problem, and prove a rule with an example. # Getting started > Install archangel, write a first table, see it find a problem, and prove a rule with an example. This page takes you from an empty config to a table that finds a real problem. It takes about ten minutes. Each output on this page is what archangel 0.3.5 prints for the files on this page. ## Before you start * You need Node 22.18 or later. Node runs `archangel.config.ts` without a build step. * You need a TypeScript or JavaScript repository. The example repository is a small shop. It has two features and some shared code: * package.json * src/ * features/ * cart/ * cart.ts * index.ts * checkout/ * checkout.ts * shared/ * money.ts ## Install archangel and write a config 1. Install the package as a development dependency: ```sh npm install --save-dev @enhance-eng/archangel ``` 2. Write a starter config: Terminal ```ansi $ npx archangel init --typescript wrote archangel.config.ts with 2 parts: features, shared It imports `@enhance-eng/archangel`, so install the package first: npm install --save-dev @enhance-eng/archangel Write what each part may import in a table, then run `archangel check`; `archangel docs may-import` shows how. ``` `init` makes one part for each folder of source files. It also adds one rule against import cycles: archangel.config.ts ```ts import { defineConfig } from '@enhance-eng/archangel'; export default defineConfig({ parts: { features: 'src/features/**', shared: 'src/shared/**', }, rules: ({ rule }) => [ rule('no-cycles', { files: '**', noCycles: true, because: 'Files that import each other cannot change or load one at a time.', examples: { accepted: ['cycle/a.ts imports cycle/b.ts'], rejected: ['cycle/a.ts imports cycle/b.ts imports cycle/a.ts'], }, }), ], }); ``` 3. Check the repository: Terminal ```ansi $ npx archangel check checked 5 files and 4 imports: no problems ``` The count includes the config file. The starter config finds no problem, because it has no rule about your architecture yet. ## Write a table A table is the main form of an architecture test. Each row of the table is a set of files. The list on the row is what those files may import. The table refuses each other import into the repository. The shop has one decision: **a feature does not import another feature.** Code that two features need goes in `shared`. 1. Replace the config with this config: archangel.config.ts ```ts import { defineConfig } from '@enhance-eng/archangel'; export default defineConfig({ parts: { feature: 'src/features/{feature}/**', shared: 'src/shared/**', }, rules: ({ mayImport, rule }) => [ mayImport('app', { feature: ['shared'], shared: [], }, { because: 'A feature changes alone. Code that two features need goes in shared.', }), rule('no-react-in-shared', { files: 'shared', mayNotImport: { packages: ['react'] }, because: 'Shared code also runs on the server.', examples: { rejected: ['src/shared/money.ts imports react'], }, }), ], }); ``` * `parts` gives names to the parts of the code. `{feature}` is a **capture**. It makes each folder in `src/features` one **instance** of the part `feature`, so `cart` and `checkout` are two instances. * The table `app` has two rows. A file in `feature` may import `shared`, and files in its own instance. A file in `shared` may import only `shared`. * `because` is your reason. Archangel shows it with each problem. * The rule `no-react-in-shared` is a rule with one condition. The next section tells you about it. 2. Check the repository again: Terminal ```ansi $ npx archangel check error[may-import]: feature may not import another instance of feature, through "../cart/cart": feature may import only shared, and itself in the same instance --> src/features/checkout/checkout.ts:1:27 | 1 | import { cartTotal } from '../cart/cart'; | ^^^^^^^^^^^^^^ feature may import only shared, and itself in the same instance | = help: remove the import, or move the code to a file whose row permits the import = note: rule: app.feature = note: because: A feature changes alone. Code that two features need goes in shared. checked 5 files and 4 imports: 1 problem For what a code means, run `archangel docs may-import`. ``` `checkout.ts` imports a file inside the cart feature. The error shows the line, the rule `app.feature`, your reason, and what the row permits. 3. Fix the problem. `checkout.ts` needs only the formatted total, so import `formatMoney` from `shared`: src/features/checkout/checkout.ts ```ts import { formatMoney } from '../../shared/money'; export function checkoutSummary(prices: number[]): string { return `Total: ${formatMoney(prices.reduce((sum, price) => sum + price, 0))}`; } ``` 4. Check again: Terminal ```ansi $ npx archangel check checked 5 files and 4 imports: no problems ``` An import through index.ts is still an import If `checkout.ts` imports `'../cart'`, the import goes to `cart/index.ts`, which is still in the instance `cart`. The table refuses it too. To permit imports through an entry file only, use [`entry`](/archangel/reference/shorthands/#entry). ## Prove a rule with an example A rule can be wrong. For example, a typing error in a path can make a rule find no files, and then it never finds a problem. So each rule that is not a table row must have a **rejected example**: code that the rule must refuse. The rule `no-react-in-shared` has one example, on one line: ```ts rule('no-react-in-shared', { files: 'shared', mayNotImport: { packages: ['react'] }, because: 'Shared code also runs on the server.', examples: { rejected: ['src/shared/money.ts imports react'], }, }) ``` Run the examples: Terminal ```ansi $ npx archangel test pass no-react-in-shared rejected #1 src/shared/money.ts 1 examples, none failed; 0 rules without a rejected example 2 table rows need no written example; `archangel rules` shows what each covers ``` Archangel makes the files of each example in memory, and checks that the rule refuses them. It does not write files. A table row needs no example, because `archangel rules` shows what each row judged, and a table that finds no file is an error. ## See what your rules do `archangel rules` shows each rule as a sentence, with the number of files and imports that it judged: Terminal ```ansi $ npx archangel rules app 2 rows archangel.config.ts:9 because: A feature changes alone. Code that two features need goes in shared. app.feature: feature may import only shared, and itself in the same instance 3 files · 3 imports judged · 1 problem · 0 exceptions app.shared: shared may import only itself 1 file · 0 imports judged · 0 problems · 0 exceptions app.every-file-has-a-kind: a file in no row is not checked here; add `files` to check it 4 files · 0 problems no-react-in-shared archangel.config.ts:16 because: Shared code also runs on the server. files in shared may not import react 1 file · 0 imports judged · 0 problems · 0 exceptions Not judged by any rule: 0 files, 0 imports between repository files. Outside packages that no rule names: none. ``` The last two lines are important. They list the files and imports that no rule judges, and the outside packages that no rule names. A file in that list can break your architecture, and no test fails. ## Understand one import To see why archangel refused an import, give the file and the line: Terminal ```ansi $ npx archangel explain src/features/checkout/checkout.ts:1 src/features/checkout/checkout.ts:1 '../cart/cart' -> src/features/cart/cart.ts file: part feature (instance checkout) target: part feature (instance cart) judged by: app.feature refuses: another instance of feature is not in its row (feature may import only shared, and itself in the same instance) skipped by: app.shared the file is not in shared app.every-file-has-a-kind this rule checks files, not imports no-react-in-shared the file is not in shared ``` ## Next steps * [Add archangel to a repository that already breaks its rules](/archangel/guides/existing-repository/). * [Use archangel in CI](/archangel/guides/ci/) and [in your editor](/archangel/guides/editors-and-agents/). * Read [Tables, rows and cells](/archangel/concepts/tables-rows-and-cells/) to write larger tables.