AI in development — a year on
For almost a year I've been writing code with AI. The main thing I've learned: it multiplies whoever understands what they're doing — and just as fast multiplies whoever doesn't.
For almost a year I've worked with AI assistants every day — not as a toy, but as part of my workflow. Over that time the thrill of "it writes the code by itself!" gave way to something more sober. AI is an amplifier. It multiplies whoever understands what they're doing, and just as fast multiplies whoever doesn't. The gap between two engineers with the same tool got bigger, not smaller.
Below is what I've arrived at in practice. This isn't universal truth — these are my working rules.
What works
Keep myself the architect and the AI the executor. The decision about a system's boundaries, its entities and how state flows stays with me. AI is great at unfolding an already-made decision into code, but trusting it with the choice of the decision means getting a plausible architecture you can't maintain. I state the intent, it proposes an implementation, I decide.
Tight feedback loops. The smaller the step, the better the result. A big, vague request gives a big, vague answer that's easy to hide a mistake in. A small request with a clear "done" criterion is verifiable. I'd rather walk the AI through ten small steps than hand it one big one.
Unfamiliar stacks and first drafts. Getting into a new library, sketching the first skeleton, getting three approaches to a problem, explaining someone else's code — here AI saves hours. My ramp-up speed for anything new has gone up several-fold.
Types and tests as guardrails. This is where the functional habit pays off directly. AI loves to write plausible code — it looks right, it compiles in your head, but it falls apart on an edge case. Strict types, explicit states and tests catch this before I do. Strictness itself comes in degrees — and here I distinguish three levels for myself.
Three levels of reliability
I build three levels of reliability into modern systems.
TypeScript — the first line. Already better than plain JavaScript: it catches a large class of errors before runtime. But it can miss some too — states that were poorly described, or that simply never got a type. On top of that TypeScript needs configuring, and correct behavior isn't guaranteed on its own. It lowers the probability of an error, but doesn't rule it out.
Effect-ts — the same TypeScript, but inside a philosophy. Namely effect types: states are described explicitly, errors are pulled out explicitly into the type (rather than hiding in exceptions), and pipe-driven development makes assembling complex systems easier. Around it is a fairly mature ecosystem of useful libraries, and it suits the backend too. This is no longer "types as hints" but types as a way to design.
Elm — a correctness guarantee at the compiler level. The top level for me: if it compiles, it works. No null or undefined, clear types and boundaries, the TEA architecture — a component of any complexity is assembled along it. A large ecosystem as an alternative to npm, strict versioning, and unusually clear and readable error messages — so clear that you can develop right at the level of compiler-driven development. The cost is its niche status: Elm is still rarely used in commercial development. But maybe in the era of LLMs something will change: when a machine writes the code, a language that physically won't let it assemble an incorrect program is worth a great deal more.
What I don't do
I don't give away understanding. If I can't explain a line, I don't merge it. The temptation to accept working code you don't understand is the most expensive trap: the debt piles up not in the code but in your head, and it surfaces the moment something breaks and there's no one left who can figure it out.
I don't confuse "compiles" with "correct". AI is optimized for plausibility, not for correctness. Its most dangerous mistakes aren't the ones that crash immediately, but the ones that look reasonable and quietly do the wrong thing. I treat its output like a junior's code at review: suspect by default.
I don't let it into the architecture unsupervised. Generating a layer that "kind of works" is easy. Realizing that layer led to a dead end can take a month. I keep the structural decisions to myself.
What's underneath
A year of working with AI didn't cancel engineering — it shifted its center of gravity. The mechanical part — typing the code — dropped to nearly zero in cost. Everything else got more expensive: the ability to frame the problem, choose the boundary, notice that a plausible answer is wrong, and take responsibility for the result. Those are exactly the skills AI can't do yet, and they're now what separates a strong engineer from a weak one.
That's why I'm not afraid AI will replace developers. I think it will replace those who treated development as a pile of code. And those who treated it as design — it will make noticeably stronger.