Building the Playground: How to Prep Your Codebase for AI Coders
How’s your codebase doing with the AI Coding taking over? Are you crying in your sleep at the thought of yet another “AI slop” PR expecting you in the morning? Or you’re trying to fight the inevitable and ban all AI contributions? I hope neither. If you are, this article might be for you.
Because in the last six months, I’ve been slowly but consistently transforming our ~300k-line-of-code repo into a nice AI-friendly playground. A place where coding humans and non-coding humans and all their agentic helpers can coexist in harmony. If you can’t beat them, join them, you know 😉.
And by AI-friendly, I don’t mean we turned on a few skills and called it a day. Our smart but oblivious to the joys of manual coding colleagues are fixing bugs and writing small- to medium-sized features as if they were born AI-native. And engineering, instead of having a nervous breakdown, quite often reacts with “yeah, that’s actually pretty good, a few small comments, and good to merge”. One day, I hope, we’ll move to “always”.
The article is basically a snapshot of my thought process superimposed on the realities of real-world coding. It all started with the “No More Any” initiative that exploded a bit in scope. I hope you hate any in TypeScript as much as I do, so you can appreciate the effort and the outcome 😉.
What Was the Problem with Any?
So, what exactly was this “any” problem? Well, in TypeScript, any type means there are no more types. It's as if I were writing the "normal" JavaScript, and who wants that? But by itself, any is not really a problem. Any experienced developer knows not to use any for anything other than hacking around tests.
The problem is, LLMs just loooove any. A tiny type failure? Well, who wants to do any due diligence and investigate the weird hallucinated mess? Just slap any at a variable as close as possible to the maximum blast radius. What can possibly go wrong? 🤦🏼♀️ More than any, they seem to love only useRef for some reason, which is another personal pet peeve of mine, but it's a completely different story.
Combine a very fast delivery pace of a pre-seed startup, with LLMs starting to take off as coding solutions, and before you know it, the repo accumulated an unspeakable number of any. More than a thousand, if you must know the number.
So two problems here:
- How to fix it?
- How to make sure something like this never happens again?
How to Fix a Thousand Any?
The fixing part was actually relatively straightforward.
In the “old” times, i.e., just a year ago, this would’ve been two months’ worth of manual labor for a poor soul who pissed off their manager too much. The most boring, mind-numbing job ever.
These days, I’m happy to report, it’s just a matter of tokens and a bit of creative thinking. “Creative thinking” because even today you can’t just command the AI “fix 1000 any for me, make no mistakes”. It will try to refactor half of the repo, will throw a few tantrums, and still will miss half of them. 1000 is just too many.
What you can do is to enable “no-explicit-any” eslint rule on a few packages and command the LLM to not stop until all the eslint rule violations are fixed. Then a few more. And more, until there is no sign of those pesky little any anywhere.
It’s actually incredibly fun to watch it do “I’ve fixed all the any! → Let me run code checks to confirm everything is fine → Oh wait, TypeScript check is broken now, let me fix that → I’ve fixed all the violations! → Oh wait, I introduced new any and eslint check is broken now, let me fix that ...". This loop can entertain a person for hours.
A few weekends for the long-running scripts with loops like this, a gasp and an almost visible eye twitch from those who paid the token bills for my shenanigans, and the repo is clean. And I hope you’re not assuming I wrote those scripts manually. There is some poetic justice in the fact that I made AI fix its own mess, I feel.
But this was the easy part. Now, the real work begins.
How to Make Sure the Repo Stays Clean?
Once the repo is clean of any, ensuring there are no more is a solved problem: I just need to keep the ESLint rule that bans them enabled. Even if one or two of them sneak past pre-commit hooks, your CI/CD pipeline will catch them, fail the pre-merge check, and the PR that tries to introduce them will be blocked from merging to main.
I hope I don’t need to preach to you that you need to have CI/CD, and to have at the very least TypeScript, ESLint, Prettier, build, and tests blocking? If some of this is missing, go fix it immediately: before building any AI-powered coding playgrounds, you need to make sure there is no broken glass on the floor and nails sticking out of the walls.
But any is just a tiny piece of the puzzle here. I also want to ban unnecessary type assertions, which LLMs love almost as much as any. But those are harder to enforce with ESLint, since sometimes they actually are necessary.
I want to make sure all the code follows the best practices I spent too much time investigating and writing about for things like data fetching, React components composition, performance, state management, and many more.
We have a design system and reusable components. I want all AI-produced code to use them instead of hardcoding new colors and buttons over and over and over 🤦🏼♀️
I want to see sensible architecture and long-term thinking in every PR, minimal weird hacks and workarounds, I want the code to be maintainable and readable, regardless of who generated it: a human or a robot.
This can’t be solved by a few ESLint and TypeScript rules. And no, it’s not about adding more skills (we have plenty).
It needs some systems thinking.
The Repo as a Self-Healing Loop
Everything is a loop these days. Agentic loops, self-healing loops, Ralph loops, auto-research loops. What if coding in the repo was also a self-healing loop?
The idea is this. Imagine AI as a three-year-old kid on the playground. If the kid tries to sneak off too far, you gently steer her back to the middle. If she’s too fast, some soft walls should stop the runaway without breaking her neck. There should be plenty of shiny toys inside to keep her busy for hours. And so on, depends on how familiar you are with the topic 😅
Or, if you prefer kittens as an analogy, we need to make sure we can do this in our repo:
CI/CD, merge blocking, TypeScript, ESLint, Prettier, tests, build — those are the minimal requirements, your soft floor of the playground, so to speak. What’s next?
Guardrail 1: Skills, Agents.md and Docs
A good, robust set of skills is pretty much mandatory for everything else. Those are the instructions and best practices you want your AI coders to follow.
Write as many docs, “how-to” guides, and architecture docs as you can for the crucial parts of the repo. Or better yet, make AI do it 😉.
Keep your Agents.md lean and reference all the docs with a short description of when to dive into one.
Make sure you include “self-healing” instructions for the docs in Agents.md. Something like “when you make changes in [bla] area, make sure you update the associated docs”. Otherwise, they will diverge from reality almost immediately.
Make sure you include links to a reference implementation. I hope you have one for every pattern you try to enforce for your AI colleagues 😉 Any non-trivial pattern, like best practices on data fetching or intricacies of design system use, needs one. Without a good, solid example, all your prose in skills is at the mercy of Great Random.
“Do this best practice” will give you somewhat good results. Sometimes. Maybe. If you’re lucky. “Do this best practice, use this part of the code as a reference and an ideal implementation” will produce consistently good results 95% of the time.
But by itself it’s still not going to be enough, of course. You think we don’t have a TypeScript best practices skill and reference code, where there is an instruction that literally says UNDER NO CIRCUMSTANCES ARE YOU ALLOWED TO USE ANY AND TYPE ASSERTIONS.
AI doesn’t care. As soon as it hits a tiny speed bump with types, AI reaches for the hacks. I have so many questions about the training data they used… 🤦🏼♀️
Skills will also be of little use if they contradict each other, or worse, contradict the actual code in the repo. You’re not happy with how data fetching is handled and want to introduce a skill that says data fetching should go through TanStack queries only? Ha. Good luck with that.
Get Nadia Makarevich’s stories in your inbox
Join Medium for free to get updates from this writer.
This skill, which is nothing more than a short text instruction injected in a massive system prompt (and it will be massive if you have many skills, this is just the way Context works), will contradict an even more massive amount of code that uses the wrong patterns. Context rot will kick in, your AI will become confused, and will produce something really weird half the time.
Skills in this case are nothing more than wishful thinking expressed as technobabble prose.
Nevertheless, they are the very first fence you need to build around the playground. Somewhat leaky, and a talented, sneaky kid will be able to escape easily if she wants, but it’s still necessary.
Without them, the reference implementation and the architecture docs, you don’t know what your definition of “good” in the repo is, and as a result, you have no aspirational goal to reach for 😉
Guardrail 2: Review Against Skills and Docs
Okay, so you have your skills, Agents.md, and architecture docs set up. Now what, considering that I just claimed that this is not enough?
We need to start closing the loop!
Write a skill that does a review of a PR against your skills. Or, to be precise, ask your favorite AI tool to do it, they are perfect for this. “Claude, pretend to be a Principal Engineer specializing in [your techstack] and write me a PR review skill that will grab the diff between the PR and main and validate whether the changes are compliant with the skills we have in the repo.”
A few iterations on this, and you’ll have your own personal PR reviewer!
A few things to keep in mind when developing a skill like this.
First of all, don’t try to replace “off-the-shelf” solutions for PR reviews like Bugbot, Claude’s reviewer, or whichever is the new hotness. Let them do their job, and focus on only one thing: compliance with the skills. As always with AI, the more focused and scoped the task is, the better it will perform.
And second, make sure the feedback is easy to grok, actionable, and actually important. Yes/no style. This must be fixed vs this part is compliant. ✅ vs 🛑. As simple and concrete as possible. The more text or “suggestions” or “nitpicks” or “consider this if you have time and energy”, the more it will pollute Context with unnecessary information, and the more chances the AI will get confused in the end. And humans, for that matter, especially non-coding humans.
This will work beautifully and will catch you many, many AI attempts to escape.
One small catch — people would need to trigger it manually to get the results. And ideally a few times. AIs are non-deterministic by their nature, always remember. Plus, different models will produce different results.
How to make sure that everyone triggers it consistently, regardless of their job description?
Continue closing the loop 😉
Guardrail 3: PR Checklist Bot
When a PR is raised, add an automation that creates a comment under the PR immediately with a checklist to follow. This should include how to run that skill.
It’s hilariously simple, but incredibly effective. Engineers will get a reminder that the skill exists and will improve by themselves.
Non-engineers will likely ask AI to “address all the comments on this PR.” The AI will then read the checklist, follow the instructions as a good compliant robot as it is, and trigger the skill. With any luck, it will address the comments as well, and the loop is almost closed.
Almost. One final step: we need to make sure the AI actually followed all those instructions with some deterministic guardrails. Otherwise, that creative but lazy genius will just pretend that the job is done half the time.
Guardrail 4: PR Bot Review Against Your Skills
Final step: set up another automation that runs as a last step before “human” review. This automation should trigger the “skills” review and post the result as the final comment before the PR goes to humans.
That way, the human reviewers will know that all the due diligence is done, the PR is actually compliant with everything the skills have to offer, and they can focus on what’s important during the review. That exact thing is up to you and your processes.
Boom, the loop is closed!
Also, now that the human reviewers are more focused, they can provide better quality reviews, notice more things to improve, and when they are annoyed enough at commenting on the same thing over and over, they will add a new skill, add it to the review process, and the loop truly becomes self-healing.
Guardrail 5: Custom ESLint Rules
One final guardrail to mention before I let you go. The most under-appreciated, incredibly efficient, and crazy easy to roll out these days with the help of AI.
Did you know that you can implement your own ESLint rules? 😉 And by “you,” of course, I mean your AI coworker. Just ask it “set me up a custom ESLint rules plugin in my repo, I want to write my own rules to enforce stuff for the sake of code consistency”.
All you need to do is familiarise yourself with the concept of what AST is and its power (and it’s endless! 😍), maybe play around with AST explorer just to get a gist of how it works, and then the world is your oyster: you can enforce any crazy idea in your repo. DETERMINISTICALLY! Isn’t it nice to have some predictability in your tools every once in a while? 😅
You want to make sure all the boolean variables follow a certain naming convention, like isBla, hasBla, or willBla? Ban hard-coded colors like #f0f0f0 in favor of design tokens via Tailwind? Ban React Server Components in favor of Tanstack Query? All of this is trivial with ESLint, and ESLint is trivial for LLMs.
The most important thing here is the error message that the rule will spit out on failure. Aim the message to be an instruction to AI. It should always provide:
- Why the rule is here and what problem it solves.
- What to do to fix the issue.
- If any skills or architecture docs need to be read, those should be linked.
Because this is the best part of AI: with all its flaws, it’s not going to be annoyed at you for yet another unreasonable ESLint failure. It will just see that the lint fails, grep for the error message, see the instruction, and execute. No arguing. No hurt feelings. Pure bliss 🙈
The Outcome
The end result? Our designer contributes PRs, and I’m not afraid anymore 😅 Some product people are fixing bugs left and right. Delivering small features end-to-end, from talking to the customers to a prototype to implementation to a PR to a compliant, quality PR to production.
Other product people are delivering full-page full-stack features with such speed and quality, I feel embarrassed. 🙈 Hey, Raz, if you’re reading this 😉
The new generation of developers will be very different, that’s for sure. More people- and product-focused at the very least.
Quite looking forward to what they’ll come up with ☺️
Originally published at https://www.developerway.com on May 22, 2026. The website has more articles like this. 😉
Subscribe to the newsletter, connect on LinkedIn, or follow on Twitter or Bluesky to get notified as soon as the next article comes out.









