AI Usage Disclosure

I use large language models in my work, including in the code in my public repositories.

What I use them for: writing and refactoring code, generating test cases, drafting documentation, reviewing my own code for edge cases I missed, exploring unfamiliar libraries, and rubber-ducking design decisions before I commit to one.

What I do not use them for: generating data, generating results, writing or inventing citations, or producing any scientific claim I have not verified against a primary source. I do not paste unpublished data, private datasets, or anything under confidentiality into a hosted model.

Who is responsible: I am. Every line in my repositories is mine in the sense that matters. If it is wrong, the mistake is mine, not the model’s.

How to treat this code: the same way you would treat any code written by one person. Read it, run the tests, open an issue if something is broken.

Why I work this way 

The labor moves, the judgment stays

Take something as small as a p-value.

By hand, you pick a test, compute the statistic, find the right printed table, locate your degrees of freedom, interpolate between rows, and report that p is somewhere below 0.05. Most of the work is arithmetic and lookup.

With a calculator, the arithmetic goes away. You still choose the test and still read the table.

In R, you write one line. t.test(x, y) returns an exact p-value, a confidence interval, and the alternative hypothesis it tested.

What survives every one of those steps is the part that was always hard: choosing the right test, checking whether its assumptions hold for your data, noticing that you are about to run twenty comparisons and need to correct for it, and knowing what the number does and does not license you to say. The manual labor became cheap. The judgment became the entire job.

Nobody argues that R users are not real statisticians. The tool absorbed the tedious part, and the freed-up time went into running more analyses and thinking harder about them.

I work with software like this constantly. Aligners, motif finders, statistical packages, plotting libraries. Each one is thousands of hours of other scientists’ work compressed into a function call I make without thinking. This is how the field works and has worked for a long time.

So an LLM is not a strange new category to me. It sits on the same continuum. What is different is the size of the jump. It makes producing a plausible-looking implementation cheap, and this makes the scarce skill becomes knowing whether the implementation is right, whether the problem was framed correctly in the first place, and what breaks when it meets real data, users, or scale.  So, we  did not stop doing the hard part. It moved, and it moved somewhere that needs a different mentality than R does, because R gives the same answer for the same computation and a model does not.

How I actually use it

These rules apply to everything, from a serious project to a weekend hobby. They change as I learn more and as new models arrive.

Research, then think, then plan, then code. The hard job here is to resist the temptation of jumping to implementation because you are excited to test your idea. I learned the hard way that doing that actually increases the chance of failure and wasting time.

Inversion path. Before I let an agent work, and again after it is done, I write down where the project can fail and which parts carry the most weight. Say the app has to be secure. Then: what are the failure points, what are the best practices, what is the logic, how do I actually make it secure? What constraints should be in place? What tests and edge cases are needed? What am I still missing?

Business logic is mine. What the code is supposed to accomplish, what counts as correct, what the failure modes mean scientifically. None of that is a code-generation problem, but my problem.

A living project spec. Each project has a directory of markdown and schemas describing what the code actually does, and sometimes it ends up bigger than the codebase. It gets updated as the code changes, and every so often I audit it again, in several passes, to keep it as true to the code and the logic as possible. It keeps the model’s context accurate, and it keeps mine accurate too. Below is an example from one of my projects.

❯ tree .
.
├── 00_project_overview.md
├── 01_aso.md
├── 02_system_architecture.md
├── 03_frontend_architecture.md
├── 04_data_models.md
├── 05_business_logic.md
├── 06_storage_strategy.md
├── 07_capacitor_plugins.md
├── 08_android_configuration.md
├── 09_ios_configuration.md
├── 10_sync_strategy.md
├── 11_conflict_resolution.md
├── 12_onboarding_flow.md
├── 13_edge_cases_and_failure_scenarios.md
├── 14_known_gaps_and_todos.md
├── 15_release_process.md
├── 16_github_actions.md
├── 17_archived_decisions.md
├── 18_deprecated_features.md
├── 19_migration_history.md
├── 20_server_side_enforcement_gaps.md
├── 21_gdpr_compliance_plan.md
└── 22_medication_module_map_and_edge_cases.md

On the state of the tools

Models today are prompt-sensitive, prone to hallucination, and inclined to tell me what I want to hear. Sometimes they are lazy and disobedient. Push them and they will often report that the job is done when it is not.

I expect this to improve, the way tooling generally improves once enough people depend on it and start feeding failures back in (pre-alpha, alpha, beta, version 1.0, etc). That is hopefully true. But until then, I am still in the driver’s seat.

* Questions about anything here, or about a specific repository, are welcome as an issue.*