A cocaine skeptic starts an unwise habit

You will be shocked to learn that this is not actually a post about cocaine
LLMs
Author
Published

July 26, 2026

AI will not take your job
A person using AI will take your job  
— Popular on LinkedIn

In almost every post written about AI, you can substitute the word “cocaine” in place of “AI” and it will make at least as much sense  
— Popular everywhere else

It’s fairly well established by now that I am not particularly fond of cocaine. In fact I’ve been quite critical of cocaine usage in the workplace, particularly for those of us who work in highly-regulated industries, using commercial-in-confidence data, and especially when that data can have very serious consequences for people’s health. Despite the relentless enthusiasm emanating from the tech sector about the potential productivity gains from using cocaine at work, I remain cautious. There are risks involved, and in my industry we are not merely morally obligated to mitigate these risks, we are legally obligated to do so as well. It does not seem at all unreasonable to suggest that unfettered cocaine usage is not a wise approach for the pharmaceuticals industry.

I am of course not literally talking about cocaine. This is about AI, and I’m expressing my worries about what happens as the pressure to use it ramps up. Because even more than other industries, the pharmaceuticals industry cannot afford to take risks with these tools. We aren’t manufacturing widgets we are designing medicines and testing their effectiveness. There is a duty of care here.1

Nevertheless the pressure to adopt AI is intensifying across the entire sector. Enticed by sparkling promises that may or may not be justified, the mysterious forces of the marketplace just … manifest. Sometimes it is stated explicitly, sometimes not. It is in the nature of the system that companies are not necessarily driven to make wise decisions; they are driven to make profitable ones. If using cocaine is profitable, a company will mandate the use of cocaine. Personally, I would not design an entire economic system around this principle, but sadly nobody asked my opinion on how the world should work.

Faced with this pressure, it is tempting to be fatalistic about the enterprise. Cocaine is coming to our workflows whether we want it or not. But I would prefer not to give in to nihilism. There are clearly some risks associated with this technology, and if I cannot avoid it entirely I would be wise to invest some serious thought into how I am going to mitigate the risks.

To put it another way, my circumstances have shifted. Until recently it was feasible to minimise my usage of the various LLM services. That’s no longer the case, and I now have to shift my strategy: instead of minimising overall usage, I’m now trying to work out how to minimise the risk to which said usage exposes me (and my company, and my clients, and my cat). This changes things considerably. I can’t minimise risk without having a firm understanding of the tools. I need to know where they can be used productively, and where they can’t. I need to know when the tool use creates risk, and when it doesn’t. To switch away from the cocaine metaphor, I’ve been asked to use a chainsaw:2 if I have to use the chainsaw, I’d better learn how to operate power tools correctly.

So inevitably it has come to pass that I have been teaching myself how to use coding agents. I’m not entirely pleased about it, but you know… market forces, babe. It’s 2026, and shareholders looooooove cocaine.3

Asserting boundaries

One of the things I found myself wanting to do at the outset, as a matter of self-care, is work out where my new boundaries would need to be set with regards to these tools. I’m no longer completely abstaining, but I don’t want them encroaching on literally everything I do. That’s not a technical issue so much as a personal one… if I don’t set clear boundaries for myself, it is inevitable that the cursed artifact will find a way to insert itself everywhere. Tool usage is infectious that way: the mere act of enabling Posit Assistant in my IDE is enough to make it feel convenient to use everywhere. I don’t want that for myself.

So, my first task was working out when I’m prepared to allow coding agents into my life. Some things are clearly “in scope” for this reluctant little experiment I’m running. Coding projects that I’ve started because of a work-induced need (e.g., emaxnls, erglm, and erplots) fall into that category. Even though they are all open source packages that I am writing and maintaining on my own time,4 they only exist in the first place because I need them professionally. And given that it is my professional working environment that creates the need for me to learn how to use coding assistants, these are fair game.

On the other end of the spectrum, other things are entirely out of scope. I tested out the idea of allowing agents to help me with writing on this blog and recoiled in absolute horror within minutes. My writing is personal. I have my own style, my own voice, my own train of thought that I follow when I write. The very moment that Posit Assistant started “helpfully” offering next-edit suggestions within this blog, I had a visceral urge to scream. I felt physically nauseous. The inline suggestions were worse than useless: not only were they completely off-track for what I was trying to write, they were intrusive, distracting, and they utterly broke my writing rhythm. It turns out that I literally cannot write when those vile things are enabled. So that’s a vehement no from me. Indeed, one of the first things I had to do once the assistant was enabled was set up the .vscode/settings.json file on this repo to block those suggestions.

They are truly awful, and oh my god I am glad they are now gone.

The other place that feels like a clear “no, absolutely not, never ever ever” decision is my generative art. It might seem counterintuitive given that it is 2026 and at this point most people think generative art is an AI thing, but that’s their problem not mine. I write art code emotionally. It isn’t software development, it isn’t data analysis, it is a personal and creative endeavour that is deeply tied to my emotional state. I’ve stopped trying to have opinions about whether AI can actually “do” art in the same sense a human does art — I simply don’t know the answer to that kind of question — but I do know that it can’t make my art. Asking Claude to help me make art is entirely incomprehensible to me. I don’t even understand the point. Art is something you do because it fulfils a personal expressive need. I can’t ask another human to do my art for me, and I can’t ask an AI to do it either. It wouldn’t be my art anymore if I did that. So I don’t.

In between these extremes, though, there are grey areas that I have struggled with.

Packages like quartose and sessioncheck are only indirectly related to my work. They were inspired by problems I encountered in my work environment, but they aren’t as tightly linked to it as the packages I mentioned at the start. After some agonising about it, I decided I’m okay with using a coding agent to help me keep them up to date and take care of basic bug fixes. It’s not really a principled decision, but a pragmatic one: I have so little free time these days that I’ve had to acknowledge that a lot of those little bugs wouldn’t get fixed if I didn’t get some assistance, and I would genuinely like to keep those packages in good working order. So there’s some light AI use allowed there. If I had the spare capacity to do it without the agent’s assistant I would, but the reality is that I don’t.

A different kind of grey area arose when thinking about my learning statistics with R book. This one is hard to talk about, because it’s emotional for me. The book itself is very old. Most of the writing dates back to the 2010-2013 period, and I have a strong visceral need to leave that text more or less intact.5 The book is… look, it has its limitations, but it is what it is and it has a history I don’t want to mess with. But at the same time, the actual framework underneath it has been a disastrous mess for over a decade. I wrote it in LaTeX originally, with no code integration at all. It had a bookdown adaptation (courtesy of Emily Kothe) but that adaptation was always incomplete, and none of us have ever had the time to fix it. And… on top of that, there is one detail about the book that has caused me personal agony for years. Something I loathe deeply, but have never been able to do anything about. The book predates my transition, and the 0.6 version that has been live for what feels like an eternity just… sits there… misgendering me and deadnaming me to the entire world, a painful reminder of a past I don’t want to revisit. I’ve spent all this time feeling hurt about that, wanting desperately to disappear the book just to make it stop. But I couldn’t. The book has been in use all this time, and other people have been relying on it. It felt selfish for me to abruptly delete the whole thing, so I chose to leave it up and live with the pain.

As it turned out, though, fixing this problem was a very good use case for coding agents. It took me a long time to do — and I burned a lot of money on tokens because holding an entire 800 page book in chat is heavy lifting even for an AI — but Claude/Posit Assistant did a good job of rewriting the book in quarto, and fixing the deadname/misgender problem. It was actually healthy for me not to do the edits by hand. Seeing my deadname and the wrong pronouns in the text of the book makes me feel sick, and there is a kind of emotional distance you get by telling the agent to take care of the problem:6 to the dispassionate machine, renaming the author is no different to renaming variables in snake case (another minor tweak I did introduce), and I told it to do both of those things as part of a general minor tidying sweep.

I still cried a lot, but less than I would have done if I’d been forced to do the work myself. And so while using a coding agent for that task does cut against the spirit of what the book is about, I have no regrets on that front. The problem is solved, the book is still there, and I never have to look at the cursed text again. I’m glad the book has been valuable to other people, delighted that people still use it, but the author needs some distance from it and from that part of her life. Version 0.7 is now live, but please don’t expect to see a version 0.8 release anytime soon.

So… yes, boundaries. Boundaries are good to have. Moving on…

On writing code with a coding assistant

Being new to the experience, I’m hardly the right person to talk about what an AI coding agent can and can’t do. I’m a novice here. I’ve worked out how to write an AGENTS.md file and I’ve used agents to help with various projects, but compared to folks who have been doing this for years I am a long way behind. So I will not offer strong opinions here; instead I’ll say something about what it has felt like to me.

Case study 1: A carefully controlled feature addition

One of the first productive things I did with these tools was work on the emaxnls package. It was something I did because of extreme time pressure. I’d built the core of the package manually, designed all the data structures, wrote the documentation, thought about the API, and indeed submitted the initial release to CRAN. Somewhat surprisingly for me, the package was in a pretty good state from the beginning. The only reason I even bothered to use an agent here was that the package I’d written only supported Emax regression models for continuous outcomes, and someone mentioned to me that they were hoping to use it to estimate Emax regression models for binary outcomes.

Extending the framework to handle the logit/bernoulli version of the Emax regression model had been on my to-do list for a long time, but I’d not had the time to do it. I’d been patient and not worried too much about the pace, but the calculus changed once I realised that people might need this functionality. Because there’s a reason I’d been a bit slow at adding this to the package: you cannot7 estimate a logit/bernoulli model by minimising a least squares problem. It will mess up your standard error estimates and your confidence intervals will be wildly unreliable. That’s the whole reason why glm() uses an iterative reweighted least squares procedure, not an ordinary least squares one. So I’d been kind of dragging my feet thinking that I really didn’t want to be arsed writing an IRLS procedure for the emaxnls package.

Except… if I don’t do it, who will? At present R doesn’t have a lot of good tools for fitting Emax regression models, so in the wild people write their own ad hoc estimation procedures using nls() to do the work… and unless they’re also writing a custom IRLS routine over the top of their nls() call,8 they won’t be getting the correct answers for binary-outcome Emax regression models. That… yeah, that seems bad?

Enter, stage left and being chased by shareholder enthusiasm, the coding agent. Deciding that it would be best if I use every tool at my disposal to prevent people from fitting models the wrong way, I asked Posit Assistant to help me design a PR that would implement binary Emax regression models as a new model class, adhering to the conventions I’d already set up in the package, and incorporating an IRLS loop into the procedure so that it yields maximum likelihood estimates rather than whatever the hell it is that happens when you close your eyes and wish real hard that your binary outcomes have normally distributed residuals.

To my pleasant suprise, it did a good job. I had to intervene a few times to stop it making some silly design choices, but nothing major. And although it was introducing a lot of new code to the package, it never felt out of control because it was mostly mimicking what I’d done previously, and the parts that were novel were things I already understood conceptually and could cross-check myself to make sure it didn’t mess up.

I still haven’t sent the updates to CRAN because I am slightly paranoid and want to spend a little more time making certain I’m happy with the result but… yeah, that one really worked. It solved a problem, and solved it quickly.

Case study 2: Oh what the hell, let’s try vibe coding

Okay. So my experience with emaxnls was a little strange, but it was controlled. At each step of the process I was able to keep track of what was happening, and stay in control of the code base. It wasn’t “vibe coding”, at least not what I take vibe coding to mean. To me, vibe coding is what happens when the coding agent starts making most of the programming decisions, the human isn’t really in control of what happens to the code, and most likely doesn’t even understand the code. That is… something different, and it terrifies me.

But in the spirit of trying to understand the capabilities and limitations of the tool, I decided to try it. Just so that I could experience what it feels like, and understand what happens during a viiiiibe coding exercise.

It will probably not surprise you to learn that I hated it.

The package that I used for this disturbing experiment is erplots. It’s an idea that I’ve had for a while, and for which I’ve had a half-written package lying around for ages. The basic idea is to supply a mini-language for creating exposure-response plots that are commonly used in pharmacometrics. I’ll spare you the tedious details, but suffice it to say I did have most of the ideas in place before handing the keys over to the AI. They just… they weren’t well implemented and I hadn’t really thought everything through yet, and I wasn’t very happy with it.

And so, deciding that the AI could hardly do much harm given that the package was a mess already, I figured I’d see what happens if I gave it some broad guidelines on what needed to be done and then let it take care of the tedious business of actually doing the work. If this sounds like a bad idea to you, dear reader, then allow me to reassure you that you’re entirely correct. It is a bad idea. But I wanted to have the experience, to see what it feels like once the AI takes over and you’re no longer really in charge of the code anymore. I knew perfectly well it would cause problems that I’d have to clean up afterwards, but of course that is the whole point of the experiment: to know what it feels like when the problems start popping up, and have the experience of seeing the mess get created in real time.

The results were interesting, I think? There’s definitely a visceral psychological sense of vertigo when you realise that you’re no longer in control. If you’re used to writing code and are used to being in control of your code, it feels frightening when the agent starts doing stuff. You can sense that you’re being sidelined, and have become little more than an observer or distant supervisor to the project. Your mental model of the codebase broke a long time ago and now you’re just being carried along for the ride. It does not feel nice.

What was most fascinating to me though, was that it does take a while for the process to really fall apart. For a good long while, Claude made some pretty reasonable choices. It extended the functionality of the package without making any major screwups, and in fact introduced some new ideas that honestly I wish I’d thought of myself.9 The bad design calls didn’t start showing up until several iterations into the process. They started happening because the AGENTS.md file (and other files I’d set up for tracking) had started to drift. After a series of bad decisions over an extended period of time,10 mismatches started to accrue between what the agent understood the design goals to be and what I understood the design goals. This became a problem. The agent started losing track of its own coding choices, and started implementing “fixes” to problems that didn’t need fixing.11 Eventually I couldn’t tolerate the distress any longer and stepped in, killing off the experiment and beginning the process of cleaning up the mess.

It’s not quite there yet, and there are still some wild things that the AI decided to chuck into the package that really really should not be there. I’ll fix those, of course. I’m not bloody sending a package to CRAN when it thinks that sure, why not use ggplot2::geom_hex() in an exposure-response plot? I may be an idiot, but I am not that much of an idiot.

Anyway, the moral of the story is that vibe coding turned out to be exactly as ghastly as I had imagined it would be.

On writing actual words with a coding agent

So now we turn to the question of using a large language model to use actual language. Writing words about code, rather than writing the code itself. On this front, I feel much more pessimistic. The kindest thing I can think to say is that Claude is… okay at doing very bland report writing? If all you need an agent to do is provide a paraphrase of some numbers in a table, it can do that. Beyond that, I’ve found them disappointing. The package documentation they write is… okay, I guess? Claude can fill out a roxygen2 skeleton appropriately if you give it instructions on the level of detail required. I’ve still had to step in and fix weird leaks where it can’t seem to grasp that AGENTS.md is internal documentation only and you should not refer to it from the package documentation, but not too often. It knows what argument types a function takes, it understands the package structure, so it can write a servicable documentation entry. I guess.

But that’s as far as it goes. Beyond those limited cases where you deliberately want it to produce bland uninspiring text that does nothing more than rephrase the information contained elsewhere, my opinions on the quality of AI writing are uniformly negative. Even the task of writing a few paragraphs into the Learning statistics with R book was beyond it. Even with my careful prompting, even with literally 800 pages of text in the required style and pitched at the target audience, it simply could not do it. It was abysmally incompetent at the task: actually worse than my already-low expectations.

It wasn’t just that book either. When writing package vignettes, the only time it was ever capable of doing the task I had clearly instructed it to do was when I indicated it was okay to talk like a software engineer: it is simply incapable of understanding that the users of an R package have a different skill set to the developers of said package, and no matter what I told it to do it would fuck it up catastrophically. It would either ignore my instructions and start rambling about the intricacies of S3 method dispatch,12 or it would go so far in the other direction that the writing sounded like the words of a condescending dick who can’t grasp that smart people sometimes have different knowledge to you. It is like… it can see that there is code in the repo, and therefore immediately decides to ignore all context and talk at me like the most annoying tech bro twat you ever met. Horrible stuff.

The only time I’ve seen it manage to do technical writing in an even half-decent way came from an open source project where we used the agent to help prepare extremely detailed plans for each document, complete with explicit instructions about voice and audience. Even then it fucked it up horribly the first time, and I had to explain the reasons why the first draft didn’t match the spec. It was only able to do a decent job after I’d painstakingly guided it through the process of creating one document that could serve as an example of how the spec should map onto the prose. At that point it was moderately capable of writing additional documents in a similar style, building off specs for those later documents. But that is an astonishing amount of scaffolding just to pull off the job of writing a half-competent professional technical document.

Honestly, I had no idea they were this bad at writing. Like, babe, you are a large language model. How in the everliving fuck in heaven are you so bad at language?

The blindingly obvious truth is that LLMs are much better at writing code than they are at writing words. If you can’t manage bland technical writing very well, you have zero chance in hell of understanding how to insert a queer footnote into the text.13 14 Admittedly, this limitation probably doesn’t pose any problems for a tech company or even a pharma company. Authorial voice is not part of the stock in trade for scientific or technical organisations, so it’s not really a dealbreaker in that sense. But letting Gemini have an attempt at writing in the style of this blog? To be vulnerable, profane, queer, and technically proficient all in one fell swoop? A virgo would not advise it.

The messiness of it all

And so we come to the end of this little self-indulgent self-reflection, and my feelings remain as mixed as they ever were. None of my ethical concerns have gone away. It is hard to love a technology built on theft and exploitation, a tool whose primary purpose is to entrench inequality and concentrate power in the hands of a handful of the worst human beings on the planet. But that could be said of almost everything in the supply chain now. You won’t find a lot of ethical conduct when you start asking why the global economy depends rather heavily on who has control over the Strait of Hormuz either. We do not live in an ethical world. I don’t love that for any of us, but I am obligated to live with it. So, like everyone else, I pretend I can’t see the horror right in front of my face and use the grotesque technology anyway. It’s not noticeably different to what I was doing already. To be alive in the 21st century is to be complicit in atrocities.

But even with ethics set gently to the side,15 I cannot help but feel conflicted the tools.

In some regards they unambiguously work. I’ve been able to accomplish some tasks very quickly, and other tasks that I’d never have even found time to start. And when controlled carefully and deployed in a context-appropriate way, coding agents are genuinely competent at some types of tasks. They’ve let me get shit done that I wouldn’t have got done without them. A lot of things, to be honest. My token spend this month has been insane. But I suspect that’s a one-off. In my life I have left behind so many dangling threads and have so many unresolved traumas just laying around on github,16 and I have not enjoyed that feeling. It has been very mildly cathartic to burn tokens with the extravagance of a gilded age oil baron, and let the machine tie up those loose ends. I am uncomfortable with the manner in which it has been accomplished, but I will take it as a win and be grateful.

In other regards, the tools remain stunningly unfit for purpose. They can code moderately well, but they do not wordsmith with any skill. And yes, often you do not need writing skill to solve a practical problem. Often you can get away with the kind of blank, soulless prose that just feels like it was written by a wooden toy that wants to be a real boy one day. You can type a prompt into midjourney and get the kind of artwork that belongs in a stockbrokers foyer as a reminder of emptiness of existence. An LLM can do those things if you ask it to.

But usually that’s not what we want from words. It’s not what we want from art. It’s not easy to encapsulate the human connection we seek from other people in a SKILL.md file. LLMs work because the statistics of the environment encode so much about the world: any sufficiently powerful learning machine will capture those statistics and reproduce them usefully if you give it the opportunity. It makes them useful tools. But I’ve never fallen love with a man because his body had the statistical structure of Michelangelo’s David. I’ve never adored a writer because her sentences so perfectly reflect the long-range dependencies between words in a corpus. I don’t stand in a photo gallery trying to think up midjourney prompts to recreate the look and feel of the Christopher Street Pier in the 1970s. Art, words, and love. Those things aren’t part of the deal. And that’s okay. That’s not what the tool is for.

Honestly the whole thing feels like a mess.

Footnotes

  1. The wildly unprofessional tone of my blog notwithstanding, I take my work extremely seriously. We do not “move fast and break things” in an industry where that kind of behaviour can put you in violation of the Nuremburg Code.↩︎

  2. Though I sometimes think a more precise metaphor would be that I’ve been put in charge of a cocaine-addled chainsaw wielding monkey, and I have to work out how to “prompt” the monkey to use the chainsaw to cut down the trees rather than to perform amateur non-consensual surgeries on innocent bystanders.↩︎

  3. Okay look that one might literally be true, based on what I understand of the people who invest in the stock market.↩︎

  4. The reader will note that nowhere in this post do I discuss anything except my personal, open source projects. This is of course deliberate, because – again, my appallingly crass writing style notwithstanding – I do actually have professional ethics.↩︎

  5. I am many things in life, but I am not George Lucas.↩︎

  6. So much so that in this case it was actually important that it be an AI doing the work and not, say, another human being. I don’t want to talk to other people about this stuff. It feels humiliating and degrading to have to ask a person to please let me be called by my own name. Getting the machine to just deal with it mechanically is emotionally easier.↩︎

  7. Well, you should not↩︎

  8. If you’ve been doing that in the exposure-response context in R, I’m genuinely impressed. Nice work.↩︎

  9. The idea of having the plotting package define generics that set up a contract with modelling packages, so that any modelling tool that supplies the appropriate methods can be visualised… yeah, I should have done that at the beginning. Kind of stupid that I didn’t, really.↩︎

  10. Yes, I’ve been watching Lucky… oh my god I love her. Plus I get some Timothy Olyphant eye candy. How does he remain exactly as hot now as he was in Deadwood a quarter of a century ago?????↩︎

  11. Specific example: at one point the agent actually forgot the reason why it had defined a generic to handle the “simulation” interface to models defined elsewhere, and didn’t realise that the erplots::er_simulate() generic that it had built was always intended to mirror the stats::simulate() generic, for which methods were carefully defined in other packages. So when it didn’t get exactly what it expected from a call to an er_simulate() method supplied elsewhere, it didn’t even consider the thought that the corresponding simulate() method for that class was probably already able to handle the situation. It was right on the verge of defining an entirely new and entirely unneded generic when I finally lost my shit and decided the vibe coding experiment was over, and some human oversight was desperately needed. And in fairness to the agent, the moment I stepped in to point out that the stats::simulate() methods that already existed for the relevant classes were richer than what erplots::er_simulate() supported, it did recover and realise the bloody obvious point that no major changes were needed and all it had to do was bring its own custom generic in line with the functionality already supported through stats::simulate(). But had I not stepped in it would have happily moved forward adding more and more machinery that was completely unnecessary.↩︎

  12. Which are not particularly intricate, so much as they are batshit↩︎

  13. You know the ones, they start out as a description of what the MIT licence means for free use of the software, and somehow end up being a discussion of consensual non-consent rules in a BDSM relationship. I mean. For example.↩︎

  14. Let’s not even get started on the opportunities afforded by my use of the word “insert” there. That’s a lay-down misère for any girl who has been passed around at the party, even on a bad day. ChatGPT could never.↩︎

  15. Because you know a story will end well when it starts with that as an opener.↩︎

  16. Not least of which is the stunning mess left behind by my forced exit from academia and the chaotic process of a transsexual mathematical psychologist trying to reinvent herself as an unremarkable pharmacometrician.↩︎

Reuse

Citation

BibTeX citation:
@online{navarro2026,
  author = {Navarro, Danielle},
  title = {A Cocaine Skeptic Starts an Unwise Habit},
  date = {2026-07-26},
  url = {https://blog.djnavarro.net/posts/2026-07-26_starting-a-cocaine-habit/},
  langid = {en}
}
For attribution, please cite this work as:
Navarro, Danielle. 2026. “A Cocaine Skeptic Starts an Unwise Habit.” July 26, 2026. https://blog.djnavarro.net/posts/2026-07-26_starting-a-cocaine-habit/.