With AI, you new objective is to implement automatic solutions to avoid sloppy/buggy code π
Well, we already have those tools, since we have the same problem with human writing code ππΌββοΈ
Three are key: types, linting, testing.
Let's see how to prevent the AI doing a mess π
Remainder: Monorepo required
As mentioned in "Full stack with Effect and AI", a monorepo is no more optional with AI.
{
"name": "app",
"private": true,
"scripts": {
"dev": "pnpm --parallel -r dev",
"build": "pnpm -r build",
"typecheck": "pnpm -r typecheck"
},
"dependencies": {
"typescript": "^5.9.2"
},
"packageManager": "[email protected]"
}A monorepo is key to share strict types (e.g. API signature between client/server), AI agents instructions (AGENTS.md/CLAUDE.md), and linting configuration.
Code will be consistent full-stack, the AI will architect full-stack and have many more examples to copy ποΈ
Strict Type Safety
Second absolutely necessary requirement: type-safety. Strict Type Safety.
In its most basic form, this comes from adding "strict": true to tsconfig.json, and a few others like exactOptionalPropertyTypes and noUncheckedIndexedAccess:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// ...
},
// ...
}Then you must follow a few guidelines for strict type checks:
- If more than 1 input parameter, use objects
- Use branded types (for nearly everything)
- Use pattern matching instead of
if/elseorswitch - Use
satisfieswhen the API only accepts primitive values - Never re-define a type, always derive (e.g.
Pick/Omit)
Instruct the AI to always run
tscafter implementing a new feature, and iterate until all type errors are fixed π«‘
Linting
Notice what I mentioned above: "...iterate until all type errors are fixed".
In the AI world, this means
as anyπ€―
That's where linting comes as another layer of safety. Add rules that are specifically designed to prevent AI from going over the board:
no-explicit-any: No moreanystrict-boolean-expressions: Only checks withbooleanprefer-nullish-coalescing: No more||no-non-null-assertion: No morenull!no-floating-promises: A classic π€eqeqeq:===and!==
Let's sprinkle in some functional programming as well:
no-param-reassignprefer-for-ofexplicit-function-return-typeorno-inferrable-typesarray-typeprefer-function-typeprefer-destructuringprefer-as-const
Once again, inform the AI to run lint while implementing a new feature, and not stop until tsc and lint pass π«΅
Testing (optional?)
Types and linting will do wonder to prevent AI mistakes. They should be enough on their own π
Then there is the last, ultimate level of safety: testing.
For quick AI-iteration purposes I mean mostly unit testing, fast and easy to check by the AI during development.
Do testing as you want/like (if necessary at all), remember to inform the AI to run test.
AIs become faster and better, but if your codebase becomes slower and more buggy there is not reward ππΌββοΈ
Make sure to define strict guardrails, and just sit back and "accept all edits".
See you next π
